個人總結(2)之基於jwt和session用戶認證的區別和優缺點

一、背景知識

Authentication和Authorization的區別:

Authentication:用戶認證,指的是驗證用戶的身份,例如你希望以小A的身份登錄,那麼應用程序需要通過用戶名和密碼確認你真的是小A。

Authorization:授權,指的是確認你的身份之後提供給你權限,例如用戶小A可以修改數據,而用戶小B只能閱讀數據。

由於http協議是無狀態的,每一次請求都無狀態。當一個用戶通過用戶名和密碼登錄了之後,他的下一個請求不會攜帶任何狀態,應用程序無法知道他的身份,那就必須重新認證。因此我們希望用戶登錄成功之後的每一次http請求,都能夠保存他的登錄狀態。

目前主流的用戶認證方法有基於token和基於session兩種方式。

  • 基於session的用戶認證

  • 基於jwt

二、基於session的用戶認證

基於session的認證流程如下:

  • 用戶輸入其登錄信息

  • 服務器驗證信息是否正確,並創建一個session,然後將其存儲在數據庫中

  • 服務器爲用戶生成一個sessionId,將具有sesssionId的Cookie將放置在用戶瀏覽器中

  • 在後續請求中,會根據數據庫驗證sessionID,如果有效,則接受請求

  • 一旦用戶註銷應用程序,會話將在客戶端和服務器端都被銷燬

三、基於token(令牌)的用戶認證

常用的是JSON Web Token(jwt):

  • 用戶輸入其登錄信息

  • 服務器驗證信息是否正確,並返回已簽名的token

  • token儲在客戶端,例如存在local storage或cookie中

  • 之後的HTTP請求都將token添加到請求頭裏

  • 服務器解碼JWT,並且如果令牌有效,則接受請求

  • 一旦用戶註銷,令牌將在客戶端被銷燬,不需要與服務器進行交互的一個關鍵是,令牌是無狀態的。後端服務器不需要保存令牌或當前session的記錄。

3.1jwt的組成

jwt的認證原理:

一個jwt實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名,這三個部分都是json格式。

頭部(Header)

頭部用於描述關於該JWT的最基本的信息,例如其類型以及簽名所用的算法等。

{
  "typ": "JWT",
  "alg": "HS256"
}

在這裏,我們說明了這是一個JWT,並且我們所用的簽名算法是HS256算法。

載荷(Payload)

載荷可以用來放一些不敏感的信息。

{
    "iss": "John Wu JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "[email protected]",
    "from_user": "B",
    "target_user": "A"
}

這裏面的前五個字段都是由JWT的標準所定義的。

  • iss: 該JWT的簽發者

  • sub: 該JWT所面向的用戶

  • aud: 接收該JWT的一方

  • exp(expires): 什麼時候過期,這裏是一個Unix時間戳

  • iat(issued at): 在什麼時候簽發的

把頭部和載荷分別進行Base64編碼之後得到兩個字符串,然後再將這兩個編碼後的字符串用英文句號.連接在一起(頭部在前),形成新的字符串:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0

簽名(signature)

最後,我們將上面拼接完的字符串用HS256算法進行加密。在加密的時候,我們還需要提供一個密鑰(secret)。加密後的內容也是一個字符串,最後這個字符串就是簽名,把這個簽名拼接在剛纔的字符串後面就能得到完整的jwt。

header部分和payload部分如果被篡改,由於篡改者不知道密鑰是什麼,也無法生成新的signature部分,服務端也就無法通過,在jwt中,消息體是透明的,使用簽名可以保證消息不被篡改。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

四、區別和優缺點:

基於session和基於jwt的方式的主要區別就是用戶的狀態保存的位置,session是保存在服務端的,而jwt是保存在客戶端的。

jwt的優點:

可擴展性好:應用程序分佈式部署的情況下,session需要做多機數據共享,通常可以存在數據庫或者redis裏面。而jwt不需要。

無狀態:jwt不在服務端存儲任何狀態。RESTful API的原則之一是無狀態,發出請求時,總會返回帶有參數的響應,不會產生附加影響。用戶的認證狀態引入這種附加影響,這破壞了這一原則。另外jwt的載荷中可以存儲一些常用信息,用於交換信息,有效地使用 JWT,可以降低服務器查詢數據庫的次數。

jwt的缺點:

安全性:由於jwt的payload是使用base64編碼的,並沒有加密,因此jwt中不能存儲敏感數據。而session的信息是存在服務端的,相對來說更安全。

性能:jwt太長。由於是無狀態使用JWT,所有的數據都被放到JWT裏,如果還要進行一些數據交換,那載荷會更大,經過編碼之後導致jwt非常長,cookie的限制大小一般是4k,cookie很可能放不下,所以jwt一般放在local storage裏面。並且用戶在系統中的每一次http請求都會把jwt攜帶在Header裏面,http請求的Header可能比Body還要大。而sessionId只是很短的一個字符串,因此使用jwt的http請求比使用session的開銷大得多。

一次性:無狀態是jwt的特點,但也導致了這個問題,jwt是一次性的。想修改裏面的內容,就必須簽發一個新的jwt.

(1)無法廢棄

通過上面jwt的驗證機制可以看出來,一旦簽發一個jwt,在到期之前就會始終有效,無法中途廢棄。例如你在payload中存儲了一些信息,當信息需要更新時,則重新簽發一個jwt,但是由於舊的jwt還沒過期,拿着這個舊的jwt依舊可以登錄,那登錄後服務端從jwt中拿到的信息就是過時的。爲了解決這個問題,我們就需要在服務端部署額外的邏輯,例如設置一個黑名單,一旦簽發了新的jwt,那麼舊的就加入黑名單(比如存到redis裏面),避免被再次使用。

(2)續簽

如果你使用jwt做會話管理,傳統的cookie續簽方案一般都是框架自帶的,session有效期30分鐘,30分鐘內如果有訪問,有效期被刷新至30分鐘。一樣的道理,要改變jwt的有效時間,就要簽發新的jwt。最簡單的一種方式是每次請求刷新jwt,即每個http請求都返回一個新的jwt。這個方法不僅暴力不優雅,而且每次請求都要做jwt的加密解密,會帶來性能問題。另一種方法是在redis中單獨爲每個jwt設置過期時間,每次訪問時刷新jwt的過期時間。

可以看出想要破解jwt一次性的特性,就需要在服務端存儲jwt的狀態。但是引入 redis 之後,就把無狀態的jwt硬生生變成了有狀態了,違背了jwt的初衷。而且這個方案和session都差不多了。

五、總結

適合使用jwt的場景:

  • 有效期短

  • 只希望被使用一次

比如,用戶註冊後發一封郵件讓其激活賬戶,通常郵件中需要有一個鏈接,這個鏈接需要具備以下的特性:能夠標識用戶,該鏈接具有時效性(通常只允許幾小時之內激活),不能被篡改以激活其他可能的賬戶,一次性的。這種場景就適合使用jwt。

而由於jwt具有一次性的特性。單點登錄和會話管理非常不適合用jwt,如果在服務端部署額外的邏輯存儲jwt的狀態,那還不如使用session。基於session有很多成熟的框架可以開箱即用,但是用jwt還要自己實現邏輯。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章