你登入一個網站時,通常只輸入一次帳號密碼。接著切到個人頁面、購物車、設定頁,網站仍然知道「這是你」。

但 HTTP 本身並不會自動記住上一個請求是誰送的。對伺服器來說,每一次 request 原本都可以是一個獨立事件。那麼,網站到底怎麼把「你剛才已經登入」這件事延續到下一個請求?

常見答案之一,就是 Token。而 JWT(JSON Web Token)則是一種很常見的 Token 格式。

先從登入流程開始

假設你登入 Norelyn:

  1. 瀏覽器把帳號與密碼送到伺服器。
  2. 伺服器驗證密碼是否正確。
  3. 驗證成功後,伺服器產生一個 JWT。
  4. 瀏覽器保存這個 Token。
  5. 之後呼叫需要登入的 API 時,把 JWT 一起送出去。
  6. 伺服器檢查 JWT 是否有效,再決定是否允許操作。

因此,密碼的主要工作是「證明一次你是誰」;JWT 則可以在後續請求中攜帶已驗證的身分資訊。

這不代表 JWT 本身就是登入系統,也不代表使用 JWT 就一定安全。它只是認證與授權架構中的一種資料格式與機制。

JWT 長什麼樣?

JWT 常看起來像一長串亂碼,而且中間有兩個句點:

xxxxx.yyyyy.zzzzz

實際上,它被分成三段:

Header.Payload.Signature

也就是:標頭、內容、簽章。

三段各自有不同用途。

第一段:Header

Header 通常描述這顆 Token 使用什麼類型與簽章演算法,例如:

{

"alg": "HS256",

"typ": "JWT"

}

alg 是 algorithm,表示簽章演算法;typ 則表示 Token 類型。

這些 JSON 資料會經過 Base64URL 編碼,形成 JWT 的第一段。

注意,是「編碼」,不是「加密」。這個差異非常重要。

第二段:Payload

Payload 放的是 claims,也就是 Token 想表達的資訊,例如:

{

"sub": "user_123",

"role": "admin",

"exp": 1789700000

}

sub 常用來表示主體,例如使用者 ID;role 可能表示權限;exp 則代表過期時間。

JWT 標準也定義了一些常見 claims,例如 iss(issuer,簽發者)、aud(audience,接收對象)、iat(issued at,簽發時間)等。

Payload 同樣通常只是 Base64URL 編碼。

所以,如果你把 JWT 貼到解碼工具裡,常常可以直接看到 Payload 的內容。這是正常現象。

因此不要把密碼、信用卡號、API Secret 等敏感秘密直接塞進普通 JWT Payload。

第三段:Signature

Signature 才是 JWT 能用來驗證「內容有沒有被竄改」的核心。

伺服器會根據 Header、Payload,加上只有驗證端知道或能驗證的金鑰資料,計算出簽章。

概念上可以想成:

signature = sign(header + payload, key)

當伺服器收到 JWT 時,可以重新驗證這個 Signature。

如果有人把 Payload 裡的:

"role": "user"

偷偷改成:

"role": "admin"

內容雖然改得動,但原本的 Signature 就對不上了。只要伺服器正確驗證簽章,這顆被修改的 Token 就應該被拒絕。

簽章不是加密

這是理解 JWT 最重要的一件事之一。

JWT 的 Signature 主要回答:

「這份資料是不是由可信任的一方簽發,而且之後沒有被修改?」

它不一定回答:

「別人能不能看到這份資料?」

一般常見的 signed JWT,也就是 JWS 型態,Header 和 Payload 可以被解碼閱讀。Signature 保護的是完整性與可信度,不是機密性。

如果資料本身必須保密,需要另外考慮 HTTPS、資料最小化,或使用具備加密設計的機制,而不是把「有簽章」誤認為「內容看不到」。

那 HTTPS 還需要嗎?

需要。

JWT 不能取代 HTTPS。

即使攻擊者無法修改 Token,如果他能在傳輸途中直接偷到一顆仍有效的 Token,就可能拿它冒充原本的使用者。

這種情況不需要破解 Signature,只需要「拿走整顆 Token」即可。

因此實際網站仍應透過 HTTPS 傳輸認證資訊,並妥善處理 Token 的儲存與生命週期。

JWT 為什麼要有過期時間?

如果一顆 Token 永遠有效,那麼只要它曾經外洩一次,風險就可能持續非常久。

因此 JWT 常設定 exp,也就是 expiration time。

例如 Access Token 可能只有效 15 分鐘或 1 小時。過期後,客戶端需要重新取得有效憑證。

這會形成一個基本安全取捨:

Token 活得越久,使用上通常越方便;但外洩後可被利用的時間也越長。

Token 活得越短,風險窗口較小;但系統就需要設計更新 Token 的流程。

Access Token 與 Refresh Token

很多系統會把 Token 分成兩種角色。

Access Token:壽命較短,用來存取 API。

Refresh Token:壽命較長,用來換取新的 Access Token。

這樣做的目的是避免把一顆長期有效的高頻憑證帶到每個 API request。

但 Refresh Token 本身也非常敏感。如果被偷走,攻擊者可能持續換出新的 Access Token。因此它通常需要更嚴格的儲存、撤銷與輪替策略。

JWT 跟 Session 有什麼不同?

傳統 Session 登入常見做法是:伺服器保存一份 session 狀態,瀏覽器只持有一個 session ID。

例如:

瀏覽器:session_id = abc123

伺服器:abc123 → user_123、已登入、其他狀態

收到請求後,伺服器用 session ID 查回資料。

JWT 則常把部分 claims 直接放在 Token 中。伺服器可以驗證簽章後取得內容,不一定需要為每一顆 Access Token 查詢 Session 資料庫。

因此 JWT 常被稱為適合 stateless authentication 的工具之一。

但「可以不查資料庫」不代表「永遠不該查資料庫」。例如使用者被停權、權限剛變更、Token 被撤銷時,系統仍可能需要額外狀態才能做出正確判斷。

JWT 並不是 Session 的升級版

JWT 與 Session 並不是「新技術一定取代舊技術」的關係。

Session 的優點之一,是伺服器掌握狀態,因此要讓某個 Session 立即失效通常很直觀:刪掉伺服器端 Session 即可。

JWT 如果完全採無狀態驗證,一顆已簽發且尚未過期的 Token 通常仍然有效。若要做到立即撤銷,就可能需要 blacklist、token version、集中式狀態或其他設計;此時系統又重新引入了一部分狀態。

所以真正的問題不是「JWT 還是 Session 哪個比較高級」,而是你的架構需要什麼。

Token 應該存在哪裡?

這是前端常遇到的問題。

有人會把 JWT 放進 localStorage,因為 JavaScript 存取方便;但如果網站發生 XSS,惡意 JavaScript 也可能讀取 Token。

另一種方式是透過 Cookie 保存認證資訊,並設定 HttpOnly,讓一般前端 JavaScript 無法直接讀取 Cookie。再搭配 Secure、SameSite 等屬性,可以降低部分攻擊面。

但 Cookie 也有自己的安全模型,例如需要正確處理 CSRF。

因此沒有一句「JWT 一律放某處」能適用所有架構。儲存方式必須和 XSS、CSRF、跨網域需求、前後端架構一起設計。

最常見的 JWT 誤解

第一個誤解是「JWT 看起來像亂碼,所以內容是加密的」。不是。Base64URL 很容易解碼。

第二個誤解是「只要能 decode JWT,就代表驗證成功」。不是。Decode 只是讀內容,真正的安全判斷必須 verify Signature,並檢查 exp、iss、aud 等必要條件。

第三個誤解是「JWT 可以取代 HTTPS」。不能。Token 被完整竊取後,攻擊者未必需要修改它。

第四個誤解是「JWT 一定比 Session 更適合」。也不是。兩者有不同的狀態管理、撤銷、擴展與安全取捨。

一個完整的 API 請求

登入成功後,前端可能拿到 Access Token。之後呼叫 API 時,常見形式是:

Authorization: Bearer <token>

伺服器收到 request 後,大致會:

  1. 取得 Bearer Token。
  2. 驗證 Signature。
  3. 檢查是否過期。
  4. 驗證 issuer、audience 等必要 claims。
  5. 取得 user ID 或權限資訊。
  6. 再進行真正的授權判斷。

最後一步尤其重要。

「Token 有效」只代表這個身分憑證通過驗證,不代表使用者可以做任何事情。

Authentication 是確認「你是誰」;Authorization 則是判斷「你能做什麼」。

JWT 真正解決的是什麼?

JWT 的價值不是讓登入變得神奇,而是提供一個標準化、可簽章、方便跨系統傳遞 claims 的 Token 格式。

它可以讓 API 在收到請求時知道:這顆 Token 由誰簽發、代表哪個主體、何時過期,以及攜帶哪些聲明。

但安全性從來不只存在 JWT 那一行程式碼裡。HTTPS、金鑰管理、Token 儲存、過期時間、撤銷策略、XSS/CSRF 防護與權限檢查,全部都屬於同一套認證系統。

所以下次看到那串 xxxxx.yyyyy.zzzzz,不要只把它當成一串登入亂碼。

它其實是一份可以被閱讀、被驗證、具有期限的身分聲明;真正讓它可信的,不是它看起來有多複雜,而是伺服器如何簽發、驗證與管理它。

把概念放回真正的系統流程裡看,通常比只背名詞更容易理解它。