JWT(Bearer Token)與 Session Cookie 是最常見的登入認證方案。本文從前端 SPA 與後端實作角度,拆解 JWT 的 access_token/refresh_token 更新流程,以及 Session Cookie 搭配後端 Session store(如 Redis)的運作方式,並比較擴充性、即時登出與撤銷能力、安全風險(XSS、CSRF)與實務選型建議,協助你在面試或專案設計時做出更合理的決策。
Written by: Chia1104 CC BY-NC-SA 4.0
今年自己在面試時,有個問題被問了非常多次『你是否有做過登入認證』,由於自己多半是找前端工程師,但自己因為寫過後端所以多半會講 JWT (Bearer token) 跟 Session Cookie 兩種,這裡特別說一下:這裡的 Session 是指後端的 Session 跟前端的 Cookie,而非前端一次性 Cookie 的 Session cookie,由於是一起使用的所以自己多半會講在一起,但蠻意外的有部分面試官仍不懂 Session Cookie。
還記得那時候一講到 Session Cookie 時那個面試官還皺了眉頭,但幸好另一旁較資深的工程師眼睛稍微亮了一下於是就轉向他繼續說...
JWT 在現代的前後端分離架構中幾乎是登入認證的標配,特別是當前端以 SPA(Single Page Application)為主時更顯方便。
它的核心概念是 無狀態,也就是後端不需要儲存使用者登入狀態,伺服器只要驗證 Token 的有效性即可。
這樣的設計讓 JWT 很適合分散式系統或多服務架構,因為不必共享 Session 狀態,也降低了伺服器的負擔。
但代價就是一旦 Token 發出,除非過期或手動黑名單,否則中途難以撤銷,這也是不少新手容易忽略的風險點。
講到 JWT,最多的做法是在 Authorization header 裡使用 Bearer token格式做發送。
典型的請求長這樣:
GET /me HTTP/1.1
Host: api.example.com
Authorization: Bearer <ACCESS_TOKEN>而這個 access_token 其實通常可以直接存在 localStorage 中,前端直接取得後放在 Header 裡即可,若要刷新 token 則用 refresh_token。
有些人會說放在 cookie 用 HTTP Only 較安全來預防 XSS (跨站腳本),但我認為若要預防 XSS 可以直接用 CSP (Content Security Policy)來預防,畢竟 Bearer <ACCESS_TOKEN> 是要前端自行在 Header 置放的,若用 HTTP Only 前端也無法取得。
相對來說,傳統的 Session Cookie 機制是 有狀態的,伺服器會在登入後產生一段 Session ID,並以 Cookie 的方式回傳給瀏覽器。 每次請求時,瀏覽器會自動附上這段 Cookie,後端再比對記錄中的 Session 資料。
因為狀態都由後端控管,所以登出、權限變更、強制下線等操作都比較容易。 這種機制在單一伺服器架構或用 Redis 做 Session 儲存時仍非常穩定,也方便控管安全性(例如設定 HttpOnly 或 Secure 標籤)。
在 Session Cookie 模式下,所謂的「refresh」,其實就是在後端延長 Session 的有效時間(sliding expiration),而不是像 JWT 那樣簽發新的 Token。
典型流程是這樣的:
登入時: 後端建立一筆 Session(例如存在記憶體或 Redis),給它一個 sessionId,並設定過期時間(例如 30 分鐘)。 回傳 Set-Cookie: sessionId=隨機字串; HttpOnly; Secure; SameSite=Strict 給瀏覽器。
每次請求時: 瀏覽器自動帶上 sessionId Cookie。 後端用這個 sessionId 去查 Session 儲存(例如 Redis),如果有資料而且沒過期,就代表使用者仍然登入中。 若專案有開「滑動過期(sliding expiration)」:只要這次請求被認定為合法,就順便把這筆 Session 的「過期時間」往後延一段(例如再延 30 分鐘)。
登出或強制下線時: 後端直接刪除該 sessionId 對應的 Session 資料。 之後就算瀏覽器還拿著舊 Cookie,也查不到 Session,自然就被當成未登入。 也就是說,Session Cookie 的 refresh 是「更新後端 Session 的過期時間」,不是重新發一顆新的 Cookie,Cookie 裡的 sessionId 通常整個登入期間都不變。
| 面向 | JWT(Bearer token) | Session Cookie |
|---|---|---|
| 狀態存放位置 | 無狀態,登入資訊放在 Token 本身,由客戶端保存。 | 有狀態,登入資訊存在伺服器的 Session store,客戶端只存 sessionId。 |
| 擴充性 | 適合多服務、跨網域、微服務等分散式架構,因為不需要共用 Session store。 | 單機或用 Redis、資料庫共享 Session 即可,對一般後台或企業內部系統很夠用。 |
| 撤銷與登出 | 純 Stateless 實作下難以即時撤銷,需要黑名單或 token version 等額外機制。 | 伺服器刪除或標記 Session 即可,強制下線、權限更新都能即時生效。 |
| 安全風險重點 | 若 Token 存在可被 JS 存取的位置,XSS 一旦成功就可能直接被竊取;體積較大、可能洩露過多資訊。 | Cookie 本身只是一組隨機 ID,可搭配 HttpOnly、Secure、SameSite 等屬性,加強對 XSS 與 CSRF 的防護。 |
| 適用情境 | SPA、行動 App、多服務或需要對外暴露 API 的場景,且有能力設計完整的 token 生命周期與安全策略時使用。 | 傳統 Web、後台管理系統、企業內部系統,或需要穩定、容易控管的登入狀態時更合適。 |
真實世界裡,JWT 和 Session Cookie 幾乎不會是「二選一」這麼單純,更多時候它們是被混搭、被包裝在更大的架構裡使用的︰例如 Web 介面用 Session Cookie 管理登入狀態,而 API Gateway 或內部微服務之間再用 JWT 來交換授權資訊。
當專案還小、只有單一後端、部署也不複雜時,Session Cookie 往往是更穩定、也更容易被團隊理解與維護的選擇;反之,當系統開始切服務、做前後端分離、甚至要對第三方開放 API 時,JWT 帶來的「自我描述、可在多個服務間驗證」的特性就會變得非常有價值。