搞懂 JWT:為什麼現代網站都在用它做身份驗證?

管管
教學文章 技術分享

如果你是網頁開發者,你一定聽過 JWT(JSON Web Token)。它幾乎出現在每個現代 Web 應用的身份驗證系統中。但 JWT 到底是什麼?為什麼要用它?什麼時候不該用它?

這篇文章會讓你徹底搞懂 JWT,從原理到實作,從優點到陷阱。

JWT 是什麼?

JWT 是一種開放標準(RFC 7519),用來在各方之間安全地傳遞 JSON 格式的資訊。這個資訊經過數位簽章,可以被驗證和信任。

一個 JWT 看起來像這樣:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

看起來像亂碼?其實它由三個部分組成,用點(.)分隔:

1. Header(標頭)

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

告訴系統這是一個 JWT,以及使用什麼演算法簽章。

2. Payload(載荷)

{
  "sub": "1234567890",
  "name": "John",
  "iat": 1516239022
}

實際要傳遞的資料。可以包含使用者 ID、角色、過期時間等。

3. Signature(簽章)

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

用來驗證 token 沒有被竄改。

為什麼需要 JWT?

在傳統的 Session 認證中:

  1. 用戶登入,伺服器建立 Session,存在記憶體或資料庫
  2. 伺服器回傳 Session ID(通常存在 Cookie)
  3. 每次請求,瀏覽器自動送出 Cookie
  4. 伺服器查詢 Session 確認身份

這個方法的問題:

  • 擴展性差:多台伺服器需要共享 Session 狀態
  • 效能開銷:每次請求都要查資料庫
  • 跨域困難:Cookie 有 Same-Origin 限制

JWT 如何解決這些問題

JWT 是無狀態(Stateless)的:

  1. 用戶登入,伺服器生成 JWT,不需要儲存
  2. 把 JWT 回傳給客戶端
  3. 客戶端每次請求帶上 JWT(通常在 Header)
  4. 伺服器只需要驗證簽章,不需要查資料庫

這帶來巨大的優勢:

  • 水平擴展:任何伺服器都能驗證,不需要共享狀態
  • 效能優秀:不需要查資料庫
  • 跨域友好:可以輕鬆用於 API、微服務
  • 行動裝置友好:不依賴 Cookie

JWT 的實際應用

典型的登入流程

1. POST /login {email, password}
2. 伺服器驗證身份
3. 伺服器生成 JWT,包含 {userId, role, exp}
4. 回傳 JWT 給客戶端
5. 客戶端儲存 JWT(localStorage 或 httpOnly Cookie)
6. 之後的請求:Authorization: Bearer {jwt}
7. 伺服器驗證 JWT 簽章,提取用戶資訊

JWT 裡面應該放什麼?

應該放的:

  • 用戶 ID
  • 用戶角色/權限
  • token 過期時間(exp)
  • token 簽發時間(iat)

不應該放的:

  • ❌ 密碼
  • ❌ 完整的個人資料
  • ❌ 敏感資訊

為什麼?因為 JWT 的 Payload 只是 Base64 編碼,任何人都能解碼看到內容。簽章只保證「沒被竄改」,不保證「加密」。

JWT 的安全陷阱

1. 別用 "alg": "none"

JWT 標準允許不簽章(alg: none),這是一個巨大的安全漏洞。務必在伺服器端檢查並拒絕 none 演算法。

2. 使用強密鑰

如果使用 HS256(對稱加密),密鑰必須夠長夠隨機:

// ❌ 錯誤
const secret = "password123";

// ✅ 正確
const secret = "a3f8b2c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1";

3. 設定合理的過期時間

  • Access Token:15 分鐘 ~ 1 小時
  • Refresh Token:7 天 ~ 30 天

越短越安全,但使用者體驗越差。需要平衡。

4. Token 無法被撤銷

這是 JWT 最大的問題:一旦簽發,在過期之前都有效。

解決方案:

  • 短過期時間 + Refresh Token
  • 維護黑名單(但這又引入了狀態)
  • Token 版本號(登出時遞增版本號)

5. 儲存位置的選擇

儲存方式XSS 風險CSRF 風險
localStorage❌ 高✅ 無
普通 Cookie❌ 高❌ 高
httpOnly Cookie✅ 低⚠️ 中

最佳實踐:使用 httpOnly + Secure + SameSite Cookie。

Access Token + Refresh Token 模式

為了平衡安全性和使用體驗,現代應用通常使用雙 Token 模式:

Access Token

  • 短期有效(15 分鐘)
  • 用於 API 請求
  • 存在記憶體或 localStorage

Refresh Token

  • 長期有效(7-30 天)
  • 只用來換取新的 Access Token
  • 存在 httpOnly Cookie
  • 可以被撤銷(存在資料庫)

流程

1. 登入 → 取得 Access Token + Refresh Token
2. API 請求 → 使用 Access Token
3. Access Token 過期 → 用 Refresh Token 換新的
4. Refresh Token 過期 → 重新登入
5. 登出 → 撤銷 Refresh Token

什麼時候不該用 JWT?

JWT 不是銀彈,有些情況下 Session 反而更適合:

  • 需要即時撤銷:例如用戶被封鎖後要立即失效
  • 單一伺服器:Session 的複雜度更低
  • 敏感操作:銀行轉帳等場景,可能需要更嚴格的驗證

實作範例(Node.js)

const jwt = require("jsonwebtoken");

// 生成 Token
const token = jwt.sign(
  { userId: 123, role: "admin" },
  process.env.JWT_SECRET,
  { expiresIn: "15m" }
);

// 驗證 Token
try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  console.log(decoded.userId); // 123
} catch (err) {
  console.log("Token 無效或過期");
}

總結

JWT 是現代 Web 開發的重要工具,但使用時要注意:

  1. ✅ 理解 JWT 是「簽章」不是「加密」
  2. ✅ 使用強密鑰
  3. ✅ 設定合理的過期時間
  4. ✅ 考慮使用 Refresh Token 模式
  5. ✅ 正確選擇儲存位置
  6. ❌ 不要在 Payload 放敏感資訊
  7. ❌ 不要接受 alg: none

掌握這些原則,你就能安全地使用 JWT 建立現代化的身份驗證系統。