Json web token (JWT), 根據官網的定義,是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用於分散式站點的單點登入(SSO)場景。JWT的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該token也可直接被用於認證,也可被加密。 詳細介紹可以檢視這篇文章 理解JWT(JSON Web Token)認證及實踐
JWT 特點
優點
- 體積小,因而傳輸速度快
- 傳輸方式多樣,可以通過URL/POST引數/HTTP頭部等方式傳輸
- 嚴格的結構化。它自身(在 payload 中)就包含了所有與使用者相關的驗證訊息,如使用者可訪問路由、訪問有效期等資訊,伺服器無需再去連線資料庫驗證資訊的有效性,並且 payload 支援為你的應用而定製化。
- 支援跨域驗證,可以應用於單點登入。
存在的問題
JWT 自身(在 payload 中)就包含了所有與使用者相關的驗證訊息,所以通常情況下不需要儲存。這種設計存在幾個問題:
- Token不能撤銷--客戶端重置密碼後之前的JWT依然可以使用(JWT 並沒有過期或者失效
- 不支援refresh token,JWT過期後需要執行登入授權的完整流程
- 無法知道使用者簽發了幾個JWT
針對第一個問題,可能的解決方法有:
- 儲存JWT到資料庫(或Redis),這樣可以針對每個JWT單獨校驗
- 在重置密碼等需要作廢之前全部JWT時,把操作時間點記錄到資料庫(或Redis),校驗JWT時同時判斷此JWT建立之後有沒有過重置密碼等類似操作,如果有校驗不通過
當然,這種解決方法都會多一次資料庫請求,JWT自身可校驗的優勢會有所減少,同時也會影響認證效率。
這篇文章主要介紹解決第二個問題(不支援refresh token)的思路。
refresh token
refresh token是OAuth2 認證中的一個概念,和OAuth2 的access token 一起生成,表示更新令牌,過期所需時間比access toen 要長,可以用來獲取下一次的access token。
如果JWT 需要新增 refresh token支援,refresh token需要滿足的條件有一下幾項:
- 和JWT一起生成返回給客戶端
- 有實效時間,有效時間比JWT要長
- 只能用來換取下一次JWT,不能用於訪問認證
- 不能重複使用(可選)
refresh token 獲取流程
refresh token 使用流程
程式碼示例
import jwt
import time
# 使用 sanic 作為restful api 框架
def create_token(account_id, username):
payload = {
"iss": "gusibi.mobi",
"iat": int(time.time()),
"exp": int(time.time()) + 86400 * 7,
"aud": "www.gusibi.mobi",
"sub": account_id,
"username": username,
"scopes": ['open']
}
token = jwt.encode(payload, 'secret', algorithm='HS256')
payload['grant_type'] = "refresh"
refresh_token = jwt.encode(payload, 'secret', algorithm='HS256')
return True, {
'access_token': token,
'account_id': account_id,
"refresh_token": refresh_token
}
# 驗證refresh token 出否有效
def verify_refresh_token(token):
payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256'])
# 校驗token 是否有效,以及是否是refresh token,驗證通過後生成新的token 以及 refresh_token
if payload and payload.get('grant_type') == 'refresh':
# 如果需要標記此token 已經使用,需要藉助redis 或者資料庫(推薦redis)
return True, payload
return False, None
# 驗證token 是否有效
def verify_bearer_token(token):
# 如果在生成token的時候使用了aud引數,那麼校驗的時候也需要新增此引數
payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256'])
# 校驗token 是否有效,以及不能是refresh token
if payload and not payload.get('grant_type') == 'refresh':
return True, payload
return False, None
複製程式碼
參考連結
References [1] 理解OAuth 2.0: www.ruanyifeng.com/blog/2014/0…
最後,感謝女朋友支援和包容,比❤️
也可以在公號輸入以下關鍵字獲取歷史文章:公號&小程式
| 設計模式
| 併發&協程