之前遇到一位老面試官,問我的問題真的有點東西
這篇文章其實源于一次我的面試經(jīng)歷。
那次我面對是一位老面試官,真的很有東西。
那次面試我和他叨叨了兩小時....我滴媽我嘴巴都干了真的。
他的提問都很有深度,可以說對我的學(xué)習(xí)之路有很大的幫助。
我記得有個問題,差不多是面了一個小時的時候他問我:Cookie、Session、Token知道的吧?
我說:知道。
那你從演進(jìn)的角度來講講 Cookie 、Session、Token?
我當(dāng)時就懵了,單獨的說我都清楚,這演進(jìn)的角度讓我一下不知從何說起。
這個面試官會從各個角度去感受我對一個知識點到底是背的,還是有自己理解的。
他提問的方式真的很有東西,他也給我反饋了很多他的理解,相談甚歡,真的。
就這個問題他從 HTTP 無狀態(tài)開始慢慢的引導(dǎo)我.....
他的這波引導(dǎo)其實就串聯(lián)起了這一系列的知識點,零散的東西就被他整理的明明白白。
所以后來的學(xué)習(xí)我都喜歡找緣由,也就是為什么。
也是我一直強調(diào)的要知道:為什么會有這個東西的存在,這個的東西是為了解決什么痛點。
起初是因為我怕面試官再問我這樣的問題。
現(xiàn)在是因為就應(yīng)該這樣學(xué)。
今兒就來捋捋之前面試官問我的這個題的。
正文
1990 年。
蒂姆·伯納斯·李創(chuàng)建了 HTTP 協(xié)議。
李老的想法是把文檔存儲在服務(wù)器中,誰需要這個文檔直接從服務(wù)器獲取即可。
按照這個思想,當(dāng)時的需求只有 GET。
并且按照拿文檔的思路:拿完了連接就可以斷了,也不需要什么交互。
所以 HTTP 起初的設(shè)計就是無狀態(tài)的。
也就是請求和請求之間是沒有關(guān)聯(lián)的。
而隨著互聯(lián)網(wǎng)的發(fā)展,交互開始興起。
人們不再滿足簡單的靜態(tài)文件獲取,各種購物、社交接踵而至。
這意味著服務(wù)器需要判斷每個請求的發(fā)起者是誰,也就是需要狀態(tài)。
你聊天總得表明你是誰,并且和誰聊吧?不然服務(wù)器可不知這聊天信息得發(fā)給誰。
你購物總得讓服務(wù)器知道是誰買了這玩意吧?
總不能你買完了下線,再上線發(fā)現(xiàn)你買的東西沒了。
這時候就是需要一種技術(shù)讓請求與請求之間建立起聯(lián)系,讓請求變得有狀態(tài)。
這技術(shù)叫 Cookie,就是一個以 Key-Value 格式存儲的小文件,保存在客戶端本地。
比如登錄之后,服務(wù)器就能設(shè)置 Cookie 返回給瀏覽器,然后保存在本地。
隨便截了個百度的,列出來的就是 key,下拉箭頭打開里面就有 value。
之后對百度的請求就可以帶著 Cookie 去訪問服務(wù)器,這里假設(shè) BAIDUID 是用戶 ID。
百度的服務(wù)器一看原來是這個 ID 啊,就知道是“我”請求了,這就有狀態(tài)了。
簡單地說 Cookie 就是存儲在本地的一份文件,每次請求都會帶上 Cookie 去訪問服務(wù)器。
所以把一些用戶信息塞到 Cookie 里,這樣服務(wù)器就能判別是哪個用戶的請求了。
注意 Cookie 是有域的劃分的,來看下這個圖:
也就是每個域下面都有各自的 Cookie ,訪問不同的網(wǎng)站帶屬于這個網(wǎng)站的 Cookie ,不會帶別人的 Cookie ,不然就亂套了。
但是 Cookie 是明文存儲在用戶本地,而且?guī)в写罅康挠脩粜畔?/span>這不太安全。
并且每次請求都需要帶這么多 Cookie 對帶寬來說也不太劃算。
Session 就解決了這個問題,Session 就是會話,它有更加廣泛的含義,在和 Cookie 這些一起談?wù)摰膱鼍埃覀儼阉M義化。
Session 就是把用戶的會話信息存儲在服務(wù)端。
然后頒發(fā)給客戶端一個 sessionId,讓客戶端之后帶著 sessionId 來請求。
這樣服務(wù)端就可以通過 sessionId 去找到這個用戶的信息,從而識別請求。
那客戶端是如何帶上 sessionId 的?
這個 sessionId 還是按照 Cookie 的形式存儲在用戶的本地,發(fā)起請求的時候帶上即可。
但是把這種狀態(tài)信息存儲到服務(wù)器中使得服務(wù)器就有狀態(tài)了。
一般我們部署在線上的服務(wù)器會有多臺來做負(fù)載均衡,也互相作為 backup。
所以如果 Session 的信息存在某一臺機器上,那么當(dāng)下一次請求被負(fù)載分到另一臺機器那就找不到這個 Session 信息了。
也就不認(rèn)得這個請求了,可能的現(xiàn)象就是告訴用戶沒登錄,那用戶不就傻了。
我這剛還登錄著呢,這就告訴我沒登錄了?
所以處理方式有 session 復(fù)制,就是服務(wù)器之間互相同步 session,這樣不論請求到哪個服務(wù)器都有用戶的信息。
不過這復(fù)制就冗余了,有額外的開銷。
還有一種就是 session sticky,其實就是把你的請求一直粘在某一個服務(wù)器上,如果你請求的一開始被指派的是 A 服務(wù)器,那么之后的所有請求都只會被指派到 A 服務(wù)器上。
但是如果 A 服務(wù)器掛了,你的請求還是會被指派到別的服務(wù)器上,這樣一來用戶登錄信息還是會丟了。
可以看到復(fù)制和 sticky 都有缺陷,所以可以把 session 放到第三方存儲,比如 Redis 里。
這樣服務(wù)器等于又沒狀態(tài)了。
而服務(wù)器的無狀態(tài)意味著可以隨意伸縮,服務(wù)集群根據(jù)流量加幾臺減幾臺,很方便。
但是把 session 放第三方存儲上只是把這個維護(hù)從服務(wù)器轉(zhuǎn)嫁到第三方身上。
第三方得保證它的高可用,不然用戶登錄信息又會丟了。
不過一般而言我們的系統(tǒng)本來就要維護(hù)的第三方存儲,所以影響不大。
小結(jié)一下:Cookie 明文存儲在本地不太安全,所以想著把用戶狀態(tài)存在服務(wù)端,而 Session 就是將用戶狀態(tài)信息保存在服務(wù)端。
就暴露 sessionId 給客戶端,這樣相對而言安全些,并且也減少了網(wǎng)絡(luò)流量。
但這樣服務(wù)端就有狀態(tài)了,難以擴展。
因此可以把 Session 放到第三方存儲上,但是等于狀態(tài)還是由服務(wù)端維護(hù)。
Token
其實仔細(xì)想想,是不是不需要在服務(wù)端存儲用戶的信息?
只需要一個能代表身份的憑證即可,一個服務(wù)端頒發(fā)給用戶憑證,之后的請求讓用戶帶著這個憑證就行。
就像我們的身份證,就代表我們。
這個憑證里面就包含了用戶的信息,有人可能怕憑證被偽造。
沒事,把憑證給簽名了,這樣我們服務(wù)器就能驗證憑證的真?zhèn)巍?/p>
和別人做不得假身份證一樣。
這種憑證叫 Token。
如果一個用戶登錄了系統(tǒng),我就返回一個 Token 給他,之后每次請求他帶著這個 Token 來就行。
服務(wù)器驗證了真?zhèn)沃竽玫?Token 里面的用戶信息就知道這個請求是誰發(fā)的了。
這樣服務(wù)器就無狀態(tài)了,是真的無狀態(tài)了,當(dāng)然客戶端有狀態(tài)了。
由客戶端來保存 Token ,這樣是最合理的,不需要在服務(wù)端冗余數(shù)據(jù)。
有了 Token 之后服務(wù)器因為無狀態(tài)所以可擴展,并且 Token 還能跨應(yīng)用使用。
比如同一個公司不同應(yīng)用之間的調(diào)用,所有應(yīng)用只要能識別這個 Token 就都能登錄。
一個 Token 就搞定了,不用每個網(wǎng)站都登錄一遍,這就是單點登錄。
如果是第三方服務(wù)提供方也更容易地提供服務(wù),只需要頒發(fā)一個 Token 給調(diào)用者即可。
Token ?簡單的說就是一個含有憑證信息的令牌,只要服務(wù)器能識別這個令牌就能根據(jù)令牌的身份進(jìn)行相應(yīng)的響應(yīng)。
其實這還蘊含了時間換空間的思想,把存儲在服務(wù)器的用戶信息暴露出去,利用簽名來驗證 Token 的真?zhèn)巍?/p>
這樣每次請求都需要耗費時間去驗簽,不過好處就是不需要存儲信息,也就是時間換空間。
最后
其實像 Cookie + Session 除了可擴展還有跨域啊、跨站偽造請求等問題。
像 Token 更加靈活,在移動端等場景也更加的適用。
有關(guān)文章所講的演進(jìn)看起來好像就是 Cookie => Session =>Token
。
不是的,這幾個東西都很有用,文章只是單從認(rèn)證這一方面來看罷了。
特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!