淺談 JWT 應用在 API 授權

在不對的時間看到對的問題,一不小心就花了太多時間在上面了。

這是蠻多人會有的疑問:JWT 已經實現無狀態 API 了,為什麼還要額外實作狀態機制?

原文

原發文者的提問,與我的回答如下:

使用者按了登出之後,JWT做了什麼?

不管 JWT 放哪,local storage / cookie / session,把它移除就行了。

照理來說,應該是要讓這個token失效的,所以會有一個地方來存放失效的token ?
不然使用者可以拿著舊的Token一直使用一直登入?

這個需求不是必要。如果有這個需求,就需要有個中央 storage 放失效的 token。要記錄在哪,看你對持久化的需求而定。一般來說,可以用快取的儲存機制,以 jti 為 key,TTL 設定到 exp 過期即可。

記錄 Storage 的詳細做法是這樣的:

首先產 token 必須要有 jtiexp claim,且 jti 要唯一,這是協定上有提到的要求。接著在註銷的時候,記錄方法如上所述:以 jti 為 key,TTL 設定到 exp 過期。

判斷授權狀態的流程如下:

  1. 驗 JWT 簽章
  2. 驗 JWT 的狀態,主要是 exp
  3. 確認 jti 是否在註銷清單中

2 很好理解,當 JWT 過期了,自然就會驗證失敗。而 3 是補足 2 的不足,若註銷清單有對應的 jti,則 3 的驗證也會失敗;但會進入 3 的判斷,會是 2 的驗證通過。註銷情境簡單結論如下:

  • exp 已到時,依然由 JWT 機制處理
  • exp 未到時,則由 jti 註銷清單處理

無狀態的討論

留言有提到發問者的需求,是需要做 session 機制,而有個留言有提到:

還要比對資料庫的話是不是就失去JWT原始設計的目的?

這是一場誤會。

實際上,JWT 設計目的可以參考 jwt.io,是安全地傳遞資訊:

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

授權則是應用情境:

Authorization: This is the most common scenario for using JWT.

授權的應用上,JWT 可以達到了大部分的人會提到的無狀態 API 機制。只是要回頭想一下,HTTP 本來就是無狀態,為何還要依靠 JWT 做無狀態?而 HTTP 為何後來要定義 Cookie 狀態管理機制(RFC 6265)?

在 Cookie 機制問世,而 JWT 還沒出來前,Cookie(或 Session)在認證授權上的應用為,保存 Client 的登入狀態,直到下個 HTTP request 進來的時候,由 Cookie 的內容與 server 的內容來判斷 Client 是否還在登入狀態,而當登出的時候就改變 Cookie 與 server 的狀態,在下一次的 request 就能得到狀態的變化,這是大家熟知的 Cookie 應用。

關鍵字:改變狀態

而 JWT 問世後,如 jwt.io 所提到的,可應用在授權上,而且可以把「已授權」與「什麼時候過期」等多種資訊放在 JWT 裡,並用簽章保護,沒有人可以竄改它的內容,這樣就可以達到無狀態--也就是狀態被固定進在 JWT 裡了。可是瑞凡,如果 server 端因為某些因素想要改變 JWT 的狀態時該怎麼辦?如,系統發現這是一個非法使用者,想把它的授權狀態註銷(RFC 7009)的時候。

發出去的 JWT 就跟潑出去的水一樣收不回來。

這時候 JWT 的無狀態特性反而變成困擾,因此才會需要額外的狀態管理機制來記錄 JWT 是否被註銷。所以如果有需求是想要改變 JWT 的狀態,自然就要思考 JWT 的方案適不適合。那要判斷這個問題最好的方法,還是要回頭看 JWT 設計的目的:安全地傳遞資訊。

比方說,Token 的持有方(如前端)需要從 JWT 裡取得使用者資訊的話,那 JWT 依然是個可行的方案。