1. 程式人生 > >程式設計師過關斬將--更加優雅的Token認證方式JWT

程式設計師過關斬將--更加優雅的Token認證方式JWT

菜菜,上次你講的cookie和session認證方式,我這次面試果然遇到了

結果怎麼樣?

結果面試官問我還有沒有更好的方式?

看來你又掛了

別說了,傷心呀。到底還有沒有更好的方式呢?

你猜?


基於Token的認證

通過上一篇你大體已經瞭解session和cookie認證了,session認證需要服務端做大量的工作來保證session資訊的一致性以及session的儲存,所以現代的web應用在認證的解決方案上更傾向於客戶端方向,cookie認證是基於客戶端方式的,但是cookie缺點也很明顯,到底有哪些缺點可以跳轉上一次的文章。那有沒有一種比較折中的方案呢?有的

把認證資訊儲存在客戶端,關鍵點就是安全的驗證,如果能解決認證資訊的安全性問題,完全可以把認證資訊儲存在客戶端,服務端完全無認證狀態,這樣的話服務端擴充套件起來要方便很多。關於資訊的安全解決方案,現在普遍的做法就是簽名機制,像微信公眾介面的驗證方式就基於簽名機制。

簽名,就是隻有資訊的傳送者才能產生的別人無法偽造的一段數字串,這段數字串同時也是對資訊的傳送者傳送資訊真實性的一個有效證明。

當用戶成功登陸系統併成功驗證有效之後,伺服器會利用某種機制產生一個token字串,這個token中可以包含很多資訊,例如來源IP,過期時間,使用者資訊等, 把這個字串下發給客戶端,客戶端在之後的每次請求中都攜帶著這個token,攜帶方式其實很自由,無論是cookie方式還是其他方式都可以,但是必須和服務端協商一致才可以。當然這裡我不推薦cookie。當服務端收到請求,取出token進行驗證(可以驗證來源ip,過期時間等資訊),如果合法則允許進行操作。

基於token的驗證方式也是現代網際網路普通使用的認證方式,那它有什麼優點嗎?

1. 支援跨域訪問,Cookie是不允許垮域訪問的,這一點對Token機制是不存在的,前提是傳輸的使用者認證資訊通過HTTP頭傳輸.

2. 無狀態:Token機制在服務端不需要儲存session資訊,因為Token自身包含了所有登入使用者的資訊,只需要在客戶端的cookie或本地介質儲存狀態資訊.

3. 解耦 不需要繫結到一個特定的身份驗證方案。Token可以在任何地方生成,只要在你的API被呼叫的時候,你可以進行Token生成呼叫即可.

4. 適用性更廣:只要是支援http協議的客戶端,就可以使用token認證。

5. 服務端只需要驗證token的安全,不必再去獲取登入使用者資訊,因為使用者的登入資訊已經在token資訊中。

6. 基於標準化:你的API可以採用標準化的 JSON Web Token (JWT). 這個標準已經存在多個後端庫(.NET, Ruby, Java,Python,PHP)和多家公司的支援(如:Firebase,Google, Microsoft).


那基於token的認證方式有哪些缺點呢?

1. 網路傳輸的資料量增大:由於token中儲存了大量的使用者和安全相關的資訊,所以比單純的cookie資訊要大很多,傳輸過程中需要消耗更多流量,佔用更多頻寬,

2. 和所有的客戶端認證方式一樣,如果想要在服務端控制token的登出有難度,而且也很難解決客戶端的劫持問題。

3. 由於token資訊在服務端增加了一次驗證資料完整性的操作,所以比session的認證方式增加了cpu的開銷。


但是整體來看,基於token的認證方式還是比session和cookie方式要有很大優勢。在所知的token認證中,jwt是一種優秀的解決方案

jwt


JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用於作為JSON物件在各方之間安全地傳輸資訊。該資訊可以被驗證和信任,因為它是數字簽名的。

一個JWT實際上就是一個字串,它由三部分組成,頭部、載荷與簽名。

頭部

header典型的由兩部分組成:token的型別(“JWT”)和演算法名稱(比如:HMAC SHA256或者RSA等等)。

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

Payload

Payload 部分也是一個JSON物件,用來存放實際需要傳遞的資料。JWT 規定了7個官方欄位,供選用。

iss (issuer):簽發人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受眾
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號

除了以上欄位之外,你完全可以新增自己想要的任何欄位,這裡還是提醒一下,由於jwt的標準,資訊是不加密的,所以一些敏感資訊最好不要新增到json裡面

{
    "Name":"菜菜",
    "Age":18
}

Signature

為了得到簽名部分,你必須有編碼過的header、編碼過的payload、一個祕鑰(這個祕鑰只有服務端知道),簽名演算法是header中指定的那個,然對它們簽名即可。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

算出簽名以後,把 Header、Payload、Signature 三個部分拼成一個字串,每個部分之間用"點"(.)分隔,就可以返回給使用者。需要提醒一下:base64是一種編碼方式,並非加密方式。

寫在最後

基於token的認證方式,大體流程為:

1. 客戶端攜帶使用者的登入憑證(一般為使用者名稱密碼)提交請求

2. 服務端收到登入請求,驗證憑證正確性,如果正確則按照協議規定生成token資訊,經過簽名並返回給客戶端

3. 客戶端收到token資訊,可以儲存在cookie或者其他地方,以後每次請求的時候都攜帶上token資訊

4. 業務伺服器收到請求,驗證token的正確性,如果正確則進行下一步操作


這裡再重複一次,無論是token認證,cookie認證,還是session認證,一旦別人拿到客戶端的標識,還是可以偽造操作。所以採用任何一種認證方式的時候請考慮加入來源ip或者白名單,過期時間,另外有條件的情況下一定要使用https。