介紹
身份驗證是每個 Web 應用程式的支柱。出現了兩種主要模式:無狀態 JWT(JSON Web 令牌) 身份驗證和 基於狀態會話 身份驗證。兩者都解決相同的問題——在後續請求中驗證用戶是誰——但它們在儲存、撤銷和安全屬性方面有根本的不同。本文提供了詳細的比較,以幫助您選擇適合您的應用程式的方法。
智威湯遜結構
JWT 是一個獨立的令牌,由三個以點分隔的 base64url 編碼段組成:
header.payload.signature
// Header
{
"alg": "HS256",
"typ": "JWT"
}
// Payload
{
"sub": "user123",
"iat": 1712345678,
"exp": 1712349278,
"role": "admin"
}
// Signature (HMAC-SHA256 of header + payload)
| 細分 | 內容 |
|---|---|
| 標題 | 演算法(HS256、RS256)與令牌類型 |
| 有效負載 | 宣告 — sub(主題)、iat(發佈於)、exp(過期)、自訂資料 |
| 簽名 | 驗證令牌未被竄改 |
由於有效負載僅採用 Base64 編碼(未加密),因此敏感資料絕不能放置在有效負載中,除非使用 JWE(JSON Web 加密)。
基於會話的身份驗證
在基於會話的身份驗證中,伺服器儲存會話資料(通常在記憶體、Redis 或資料庫中)。客戶端僅在 cookie 中保存會話 ID(隨機的不透明字串):
頭對頭比較
| 方面 | 智威湯遜 | 會議 |
|---|---|---|
| 儲存位置 | 客戶端持有所有資料 | 伺服器保存會話資料 |
| 伺服器狀態 | 無狀態 — 無需資料庫查找 | 有狀態 — 需要會話儲存查詢 |
| 可擴充性 | 非常好 - 沒有共用會話儲存 | 多實例需要集中式Redis/DB |
| 撤銷 | 困難 — 有效期限至 exp | 立即 — 從儲存中刪除會話 |
| 代幣大小 | 更大(數百位元組) | 小(會話 ID ~32 位元組) |
| XSS 漏洞 | JS (localStorage) 可存取的令牌 | JS 無法存取 httpOnly cookie |
| CSRF 保護 | 需要人工處理 | SameSite cookie + CSRF 令牌 |
JWT 的撤銷挑戰
JWT 最顯著的弱點是撤銷。一旦發出,JWT 將保持有效,直到其 exp 聲明為止。如果使用者登出、更改密碼或被禁止,則在沒有伺服器端狀態的情況下令牌無法失效,這違反了無狀態前提。
緩解策略
1.短期存取令牌+刷新令牌:
// Access token: 15 minutes
// Refresh token: 7 days (stored in database)
訪問令牌的 TTL 很短,因此如果洩露,損害是有限的。刷新令牌儲存在伺服器端,因此可以撤銷。
2.令牌黑名單(拒絕名單):
// On logout, add jti (JWT ID) to Redis with TTL matching token expiry
await redis.set(`blacklist:${jti}`, 'true', 'EX', remainingTTL);
這會重新引入伺服器狀態,但僅針對已撤銷的令牌,而不是所有活動會話。
3.輪換令牌簽署金鑰:
更改簽名密鑰會使所有令牌失效——這是一種生硬但有效的緊急工具。
XSS 和 XSRF 注意事項
| 威脅 | JWT(本地儲存) | 會話(httpOnly cookie) |
|---|---|---|
| XSS | 令牌透過 document.cookie 或 localStorage 被盜 | JS 無法存取令牌(僅 http) |
| CSRF | 不容易受到攻擊(手動授權標頭) | 沒有 SameSite 或 CSRF 令牌就容易受到攻擊 |
儲存在 localStorage 中的 JWT 容易受到 XSS - 任何注入的腳本都可以讀取它們。 HttpOnly cookie 可防止這種情況,但需要 CSRF 保護,因為 cookie 是自動發送的。
JWT 的最佳實踐: 將存取權杖儲存在 httpOnly、Secure、SameSite=Strict cookie(不是 localStorage)中。伺服器從 Authorization 標頭(而不是 cookie)讀取它,以避免 CSRF。
// Set JWT in httpOnly cookie
res.cookie('token', jwt, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 15 * 60 * 1000
});
混合方法
許多生產系統結合了這兩種模式:
| 方法 | 存取令牌 | 刷新令牌 |
|---|---|---|
| 僅限智威湯遜 | 內存中的 JWT | 帶有長 exp 的 JWT |
| 會話式 | 不透明會話 ID | — |
| 混合 | 短暫的 JWT(15 分鐘) | 資料庫中不透明的刷新令牌 |
混合模型為您提供 JWT 對於大多數請求的無狀態效率,同時透過刷新令牌保留撤銷功能。
決策矩陣
| 您的首要任務 | 推薦方法 |
|---|---|
| 最小的伺服器資料庫負載 | JWT(無狀態) |
| 即時使用者登出/禁止 | 基於會話的 |
| 微服務/API網關 | JWT(傳遞) |
| 行動應用程式 | JWT(無 cookie 儲存) |
| 伺服器渲染的 Web 應用程式 | 會話(httpOnly cookie) |
| 第三方API存取 | JWT(不記名令牌) |
結論
沒有普遍的贏家。 JWT 在分散式系統、行動應用程式和 API 優先架構方面表現出色,在這些架構中,無狀態性非常有價值。會話驗證為撤銷、登出和伺服器端安全實施提供進階控制。混合模式——由可撤銷刷新令牌支援的短期 JWT——提供了一種務實的折衷方案,既發揮了兩種方法的優點,也減輕了它們的缺點。

