cookie、session和token的概念


Cookie、Session和Token都是為了解決Web身份校驗而產生的,這里對它們的概念做一個簡單了解。

Web身份校驗的發展

很久很久以前,Web基本上就是文檔的瀏覽而已。既然是瀏覽,作為服務器,不需要記錄誰在某一段時間里都瀏覽了什么文檔,每次請求都是一個新的HTTP協議,就是請求加響應。並不需要記住是誰剛剛發了HTTP請求,每個請求對服務器來說都是全新的。

但是隨着交互式Web應用的興起,像在線購物網站,需要登陸的網站等等,馬上就面臨一個問題,那就是要管理會話,必須要記住是哪些用戶登陸了系統,哪些用戶往自己的購物車中放了商品。意思就是說,服務器必須把每個用戶區分開,這就是一個不小的挑戰,因為HTTP請求是無狀態的,於是有人就想出了一個辦法,這個辦法就是給客戶端發一個會話標識(sessionId,就是一個隨機的字符串),每個客戶端收到的都不一樣,每次客戶端向服務端發起HTTP請求的時候,就會把這個隨機字符串(sessionId)一並捎過來,這樣就能區分開是哪個用戶發起的請求了。

這樣,服務器就增加了壓力,因為服務器需要保存所有用戶的sessionId。如果有上千上萬個sessionId,對服務器來說就是一個巨大的開銷,嚴重地限制了服務器的擴展能力。比如說用兩個機器組成了一個集群,小明通過機器A登陸了系統,那sessionId就會保存在機器A上,假設小明的下一次請求被轉發到機器B怎么辦?機器B可沒有小F的sessionId啊。有時候會采用session sticky的小技巧,就是讓小明的請求一直粘連在機器A上。但是這也不管用,要是機器A掛掉了,還得轉到機器B上去。

為了解決A機器掛掉的問題,只好做sessionId的復制了,把sessionId在兩個機器之間搬來搬去。

再后來,有個叫Memcached的小伙子支了招:把sessionId集中存儲到一個地方,所有的機器都來訪問這個地方的數據,這樣就不用復制了。但是這樣則增加了單點失敗的可能性,要是那個負責session的機器掛了,所有的用戶都要重新登陸一遍,估計得被人罵死。

也嘗試把這個單點的機器也搞出集群,增加可靠性。但不管如何,這小小的session對服務器來說都是一個沉重的負擔。

於是,就有人提出了擺脫session的想法。具體就是不再在服務端保存sessionId了,讓客戶端去保存一個服務端生成的token,每次請求的時候附加上這個token,服務端只要對這個token進行相應的校驗就可以完成身份驗證。比如說,小明已經登陸了系統,服務端給他返回了一個令牌(token),里面包含了小明的userId,下一次小明再通過HTTP請求訪問服務端的時候,就把這個token通過HTTP的Header帶過來就可以了。

因為服務端不再存sessionId了,也不會存token,那么就可能會造成有心懷不軌的人偽造token來做壞事。因此,服務端需要對token做一些防偽造的操作,具體是對數據做一個簽名。比如說用HMAC-SHA256算法加上一個只有服務器知道的密鑰,對數據做一個簽名,然后把簽名和數據一起作為token,由於密鑰別人不知道,就無法偽造token了。

服務器並不會保存這個token,當小明再次將token發到服務器的時候,服務端會用同樣的HMAC-SHA256算法和同樣的密鑰去對數據再計算一次簽名,並和token中的簽名做一個比較,如果相同的話,就可以判斷出小明已經登陸過,並且可以直接取到小明的userId;如果不相同,則數據部分肯定被人篡改過,這時就能夠做一些身份校驗失敗的相應處理。

 

token中的數據默認是明文保存的(雖然會用Base64URL算法做下編碼,但這並不是加密),因此最好不要在其中保存像密碼之類的敏感信息。當然了,如果一個用戶的token被別人偷走了,服務器並沒有辦法去辨別,只會認為小偷就是合法的用戶,這其實和一個用戶的sessionId被別人偷走是一樣的。

這樣一來,服務器就不需要保存sessionId了,只需要生成token,然后校驗token,相當於用CPU計算時間換回了存儲空間。解決了sessionId這個負擔,可以說是無事一身輕。機器集群現在可以輕松地做水平擴展,用戶訪問量增大的時候直接加機器就OK。

Cookie

Cookie是一個非常具體的東西,指的就是瀏覽器里面能永久存儲的一種數據,僅僅是瀏覽器實現的一種數據存儲功能。

Cookie是由服務器生成,發送給瀏覽器,瀏覽器把Cookie以Key-Value的形式保存到某個目錄下的文本文件內,下一次請求同一個網站的時候會把該Cookie發送給服務器。由於Cookie是存在客戶端上的,所以瀏覽器加入了一些限制確保Cookie不會被惡意使用,同時不會占據太多的磁盤空間,每個域的Cookie數量都是有限的。

Session

Session從字面上講,就是會話。這個就類似於與一個人交談,你怎么知道當前和你交談的是張三而不是李四呢?對方肯定有某種特征(長相等)表明他就是張三。Session也是類似的道理,服務器要知道當前發送請求給自己的是誰。為了做這種區分,服務器就要給每個客戶端分配不同的身份標識,然后客戶端每次向服務器發送請求的時候,都會帶上這個身份標識,服務器就會知道這個請求是來自於誰了。至於客戶端怎么保存這個身份標識,可以有很多方式,對於瀏覽器客戶端來說一般都默認采用Cookie的方式。

服務器使用Session把用戶的信息臨時保存在了服務器上,用戶離開網站之后Session會被銷毀。這種用戶信息存儲方式相對Cookie來說更加安全,可是Session有一種缺陷,如果Web服務器做了負載均衡,那么下一個操作請求到了另一台服務器的時候Session就會丟失。

Token

在Web領域基於Token的身份驗證隨處可見。在大多數使用Web API的互聯網公司中,Token是多用戶下處理認證的最佳方式。大部分你見到過的API和Web應用都是使用Token,例如Facebook、Twitter、Google+和Github等。

在介紹基於Token的身份驗證的原理與優勢之前,不妨先看看之前的認證都是怎么做的。我們都是知道HTTP協議是無狀態的,這種無狀態意味着程序需要驗證每一次請求,從而辨別客戶端的身份。在這之前,程序都是通過在服務端存儲的登陸信息(Session)來辨別請求的。隨着Web應用程序以及移動端的興起,這種驗證方式逐漸暴露出了問題,尤其是在可擴展性方面。

基於服務器驗證方式暴露的一些問題

1.Session:每次認證用戶發起請求時,服務器需要去創建一個記錄來存儲信息。當越來越多的用戶發請求時,內存的開銷也會不斷增加。

2.可擴展性:在服務端的內存中使用Session存儲登陸信息,伴隨而來的是可擴展性問題。

3.CORS(跨域資源共享):當我們需要讓數據跨多台移動設備上使用的時候,跨域資源的共享就會是一個讓人頭疼的問題。在使用Ajax抓取另一個域的資源,就可能會出現禁止請求的情況。

4.CSRF(跨域請求偽造):用戶在訪問銀行網站時,這種驗證方式很容易受到跨站請求偽造的攻擊,並且能夠被利用其訪問其他的網站。

在這些問題中,可擴展性是最突出的,因此我們有必要去尋求一種更加行之有效的方法。

基於Token的驗證方式

基於Token的身份驗證是無狀態的,不將用戶信息存在服務器或Session中,這種概念就解決了在服務端存儲信息時的很多問題。No Session意味着程序可以根據需要去增減機器,而不用去擔心用戶是否登陸的問題。基於Token的身份驗證的過程如下:

1.用戶通過用戶名和密碼發送請求。

2.程序驗證登陸信息,並返回一個簽名的Token給客戶端。

3.客戶端存儲Token。

4.客戶端再次發起HTTP請求,將Token附加在請求中,服務端驗證Token並返回數據。

Token的優勢

1.無狀態、可擴展。在客戶端存儲的Token是無狀態的,並且能夠被擴展。基於這種無狀態和不存儲Session信息,負載均衡器就能夠將用戶信息從一個服務傳到其他服務器上。如果我們將已驗證的用戶的信息保存在Session中,則每次請求都需要用戶向已驗證的服務器發送驗證信息(稱為Session親和性),在用戶量大的時候,可能會造成一些擁堵(集群中的某一台服務器壓力過大)。使用Token則不會有這些問題,因為Token不需要依賴特定的服務器。

2.支持移動設備。移動設備只需要有存儲Token的能力,就能夠使用這種身份驗證的方式。

3.跨程序調用。只要用戶有了一個驗證通過的Token,數據和資源就能夠在任何有相同校驗規則的服務器(域)上請求到。

4.安全性較高。請求中發送Token而不再是發送Cookie,這樣就能夠防止CSRF(跨站請求偽造)。即使在客戶端使用Cookie存儲Token,Cookie也僅僅是一個存儲機制而不是用於認證。不將信息存儲在Session中,讓我們少了對Session的操作。Token是有時效的,一段時間之后用戶就需要重新驗證。同時,Token還能有撤回的操作,可以通過Token Revocatation使一個特定的Token或一組Token失效。

 

"人生就像騎單車,要想保持平衡就得始終往前走,於是昨天越來越多,明天越來越少。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM