搞不懂Session、Cookie、Token有何關係和區別?軟體測試開發技術棧2019-10-11 20:47:03

前段時間,剛好大致的瞭解過這三者的區別。為了方便理解,我們可以從傳統的Session到Token的身份驗證演變過程,來理解Session、Cookie、Token之間的關係。

搞不懂Session、Cookie、Token有何關係和區別?

很久很久以前,Web應用基本用於文件的瀏覽,例如,網路黃頁。既然是瀏覽,伺服器也就不需要去記錄具體使用者在哪段時間裡瀏覽了哪些文件,每一次請求都是一個新的HTTP協議,對伺服器來說都是全新的,這時候沒有Session、Cookie、Token。

基於Session的身份驗證

隨著互動式Web應用的發展,比如,像購物等需要使用者登入的網站。引出了一個問題,

那就是網站需要記錄哪些使用者登入了系統進行了哪些操作

,即要進行管理會話(什麼是會話?簡單的講如果使用者需要登入,那麼就可以簡單的理解為會話,如果不需要登入,那麼就是簡單的連線。),比如,不同使用者將不同商品加入到自己的購物車中, 也就是說必須將每個使用者區分開。但因為HTTP請求是無狀態的,所以想出了一個解決辦法,那就是

給每個使用者發一個會話標識

(Session id),簡單的說就是給每個使用者發一個既不重複,又不容易被找到規律進行仿造的隨機字串,以使每個使用者收到的會話標識都不一樣。 這樣每次使用者從客戶端向服務端發起HTTP請求的時候,把這個字串給一起傳送過來, 服務端就可以區分開誰是誰了。那麼客戶端(瀏覽器)如何儲存這個“身份標識”的呢?,一般預設採用 Cookie(儲存在瀏覽器目錄中的文字檔案) 的方式,這個會話標識(Session id)會存在客戶端的Cookie中。

雖然上面的方法解決了區分使用者的問題,但同時也引入了一個新的問題,那就是每個使用者(客戶端)只需要儲存自己的會話標識(Session id),而

服務端則要儲存所有使用者的會話標識(Session id)

。 當訪問服務端的使用者逐漸變多,服務端那就需要儲存成千上萬,甚至幾千萬個,這對伺服器說是一個難以接受的開銷 。我們再舉一個簡單的例子,假如服務端是由2臺伺服器組成的一個叢集, 小明透過伺服器A登入了系統, 那 Session id會儲存在伺服器A上, 假設小明的下一次請求被轉發到伺服器B怎麼辦? 伺服器B可沒有小明 的 Session id。

可能會有人講,如果使小明登入時,始終在伺服器A上進行登入(sticky session),豈不解決了這個問題?那如果伺服器A掛掉怎麼辦呢? 還是會將小明的請求轉發到伺服器B上,問題仍然存在。

如此一來,那隻能做叢集間的 Session 複製共享了, 就是把 Session id 在兩個機器之間進行復制,如下圖,但這又對伺服器的效能和記憶體提出了巨大的挑戰。

搞不懂Session、Cookie、Token有何關係和區別?

因此,又想到如果將所有使用者的Session集中儲存呢,也就想到了快取服務Memcached——由於 Memcached 是分散式的記憶體物件快取系統,因此可以用來實現 Session 同步。把Session id 集中儲存到一臺伺服器上, 所有的伺服器都來訪問這個地方的資料, 如此就避免了複製的方式, 但是這種“集萬千寵愛於一身”使得又出現了單點故障的可能, 就是說這個負責儲存 Session 的伺服器掛了, 所有使用者都得重新登入一遍, 這是使用者難以接受的。

搞不懂Session、Cookie、Token有何關係和區別?

那麼索性將儲存Session的伺服器也設計為叢集,增加可靠性,避免單點故障,但無論如何,Session 引發出來的問題似乎層出不窮。

於是有人開始思考, 為什麼服務端必須要儲存 Session呢, 只讓每個客戶端去儲存不行嗎?可是服務端如果不儲存這些Session id ,又將如何驗證客戶端傳送的 Session id 的確是服務端生成的呢? 如果不驗證,服務端無法判斷是否是合法登入的使用者,對,這裡的問題是驗證, session 只是解決這個驗證問題的而產生的一個解決方案,是否還有其它方案呢?——於是,引出了基於Token 的身份驗證。

基於Token 的身份驗證

例如, 張三已經登入了系統,服務端給他發一個令牌(Token), 裡邊包含了張三的 user id, 後續張三再次透過 Http 請求訪問伺服器的時候, 把這個 Token 透過 Http header 帶過來不就可以了。

服務端只需要驗證 Token是自己生成的,而非偽造的。假如不驗證任何人都可以偽造,那麼這個令牌(token)和 session id沒有本質區別,如何讓別人偽造不了?那就對資料做一個簽名(Sign)吧, 比如說服務端用 HMAC-SHA256 加密演算法,再加上一個只有服務端才知道的金鑰, 對資料做一個簽名, 把這個簽名和資料一起作為 Token 發給客戶端, 客戶端收到 Token 以後可以把它儲存起來,比如儲存在 Cookie(儲存在瀏覽器目錄中的文字檔案)中,由於金鑰除了服務端任何其他使用者都不知道, 就無法偽造令牌(Token)。

搞不懂Session、Cookie、Token有何關係和區別?

這樣一來,服務端就可以不儲存 Token 了, 當張三把這個Token發給服務端時,服務端使用相同的HMAC-SHA256 演算法和金鑰,對資料再計算一次簽名, 與 Token 中的簽名做個比較, 如果兩者相同,說明張三已經登入過了, 即驗證成功。若不相同, 那麼說明這個請求是偽造的。

搞不懂Session、Cookie、Token有何關係和區別?

這樣一來, 服務端只需要生成 Token,而不需要儲存Token, 只是驗證Token就好了 ,也就實現了時間換取空間(CPU計算時間換取Session 儲存空間)。沒了Session id 的限制, 當用戶訪問量增大, 直接加機器就可以輕鬆地做水平擴充套件,也極大的提高了可擴充套件性。