淺談 JWT (Json Web Token)

前言

當服務開始龐大,server 開始 scale out,client 端每次的請求可能會交由不同的 server 處理,傳統的 session 模式必須解決 session consistency 的問題,常見的用法可以使用 Memcached 或是 Redis server 來管理。而 JWT 則提供了另一種身份認證的解決方案,有別於 session 具狀態性(Stateful)的形式,jwt 是無狀態的(Stateless),也就是不用考慮今天請求被分配到哪一台 server,根據隨著請求而來的 token ,server 就可以知道 client 是誰。

client 使用帳號密碼登入或是第三方登入,經 server 認證其身份,驗證成功後回傳一組 JWT 給 client,之後 client 在發起請求時都應夾帶這組 JWT 給 server 核查身份。過程中 server 都不用向資料庫發出查詢,也不用額外紀錄或是開啟 session,並且任何 server 拿到 token 都可以辨別其身份,過程如下圖:

JWT 驗證流程

JWT 的組成分成3個部份:header、payload、signature,結構如下:

1
Header.Payload.Signature

header 夾帶兩個資訊,一個是類型,這裡就是指 JWT,第二個為使用的演算法,如 HMAC、SHA256、RSA。

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

然後將它用 base64 編碼,此為 JWT 的第一部份。

Payload

payload 用來存放需要傳遞的資料,RFC7519 列有7個官方推薦的參數,但是這些為非必要項目,沒有也沒關係。

變量名 英文全寫 備註
iss Issuer JWT 的核發者
sub Subject JWT 的主體或者用戶
aud Audience 接收 JWT 的用戶
exp Expiration Time 過期時間(單位為秒)
nbf Not Before 開始時間(單位為秒),在該時間之前無效
iat Issued At 發佈時間(單位為秒)
jti JWT ID JWT唯一標識,區分不同發布者的統一的標識

除此之外,也可以自己定義要夾帶的其他資訊,例如:

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

再來,一樣也是把 payload 用 base64 編碼,此為 JWT 的第二個部份。

Signature

第三個來做簽章(signature),簽章是 JWT 的核心,是為了避免有人更改 payload ,偽造身份發出請求,除非 secret 外洩,否則偽造者無法做出合法的簽章。作法是使用剛剛 base64 後的 header 以及 base64 後的 payload 和一個只有 server 才知道的 secret ,使用的演算法就是 header 裡面指定的演算法,作法如下:

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

最後把 header、payload、signature 用 .組起來,JWT 就完成了,可以直接來個線上測試

使用方法

client 發起請求的時候,header 需夾帶下面的資訊才能通過身份驗證。

1
Authorization: Bearer <token>

注意事項

  • JWT 代表了一個人的認證身份,任何人拿到它都可以盜用其身份對 server 發出請求,故 token 應該安全保存,並且存活的有效期不應設太長,對於一些比較重要的應用應該要再驗證一次身份。
  • 任何人拿到 token 都可以解析 payload 的內容,除非有額外做加密處理,否則不應夾帶敏感資訊。
  • 一但核發出去的 token ,除非後端有額外的機制,否則無法註銷,只能等待其過期。
  • 根據上一點,故也無法實踐單點登入。
  • 應結合SSL使用。
  • 不要重複造輪子,善用官方建議的 Library

結論

JWT 不是萬靈丹,有其優點也有其缺點,端看應用是否適合,並做好安全性的防護。
有關 JWT 的儲存方式各有各的說法,下次再開一篇新章討論。

Reference

Introduction to JSON Web Tokens
JSON Web Token 入門教程