要理解 JWT(JSON Web Token)登錄流程中前端與后端的職責分工,需先明確 JWT 的核心定位:它是一種無狀態的身份認證令牌,用于替代傳統 Session 認證,解決跨服務、跨域登錄的問題。其流程本質是“后端生成令牌 → 前端存儲并攜帶令牌 → 后端驗證令牌”的閉環,以下分步驟拆解兩端的具體操作。
一、前置概念:JWT 的結構與作用
在講解流程前,需先明確 JWT 的組成(共 3 部分,用.
分隔),這是理解后續驗證邏輯的基礎:
部分 | 名稱 | 作用 |
---|---|---|
第一部分 | Header(頭部) | 聲明令牌類型(JWT)和簽名算法(如 HS256、RS256),Base64 編碼(可解碼) |
第二部分 | Payload(載荷) | 存儲非敏感的用戶身份信息(如 userID、角色、過期時間 exp),Base64 編碼(可解碼) |
第三部分 | Signature(簽名) | 用 Header 指定的算法,結合后端密鑰對前兩部分加密,確保令牌未被篡改 |
?? 關鍵提醒:Payload 是 Base64 編碼(不是加密),任何人都能解碼,因此絕對不能存密碼、手機號等敏感信息!
二、JWT 登錄完整流程:前端 + 后端分工
JWT 登錄流程分為 3 個核心階段:登錄請求階段、令牌存儲與攜帶階段、接口訪問驗證階段。兩端在每個階段的操作明確且互補,具體如下:
階段 1:登錄請求階段(用戶輸入賬號密碼,獲取 JWT)
這是流程的起點,目標是讓后端驗證用戶身份,并生成合法的 JWT 令牌返回給前端。
1.1 前端做什么?
核心動作:收集用戶信息 → 發送登錄請求 → 接收并暫存令牌
- 收集并校驗用戶輸入:
- 通過登錄表單獲取用戶輸入的賬號(如手機號/郵箱)、密碼;
- 前端做基礎合法性校驗(非空、格式驗證,如手機號是否符合 11 位規則),避免無效請求占用后端資源。
- 發送 POST 登錄請求:
- 向后端登錄接口(如
/api/login
)發送請求,請求體攜帶用戶信息(通常用 JSON 格式); - 示例請求體:
{"username": "zhangsan","password": "123456aA!" // 實際項目中需加密(如 HTTPS + 前端簡單哈希),避免明文傳輸 }
- 向后端登錄接口(如
- 接收后端響應并處理:
- 若登錄失敗(如賬號密碼錯誤):提示用戶“用戶名或密碼錯誤”,流程終止;
- 若登錄成功:后端會返回 JWT 令牌(通常在響應體的
token
字段,或 Header 的Authorization
字段),前端需暫存令牌(下一步會講持久化存儲),并跳轉至首頁/登錄后的頁面。
1.2 后端做什么?
核心動作:驗證用戶身份 → 生成 JWT 令牌 → 返回令牌
- 接收并驗證用戶身份:
- 接收前端發送的賬號密碼,先對密碼進行解密(若前端加密);
- 從數據庫中查詢該賬號對應的記錄,對比加密后的密碼(后端存儲密碼必須用哈希算法加密,如 BCrypt,絕對不能存明文);
- 若賬號不存在或密碼不匹配,返回 401(Unauthorized)錯誤,附帶失敗信息。
- 生成 JWT 令牌:
- 若身份驗證通過,構建 JWT 的
Payload
(載荷),必須包含過期時間(exp)(如當前時間 + 2 小時,避免令牌永久有效),可選包含userID
、role
(角色,用于權限控制)等非敏感信息; - 用 Header 中指定的算法(如 HS256),結合后端唯一密鑰(Secret Key) 對
Header
+.
+Payload
進行簽名,生成完整的 JWT 令牌;
?? 關鍵:后端密鑰必須安全存儲(如配置中心、環境變量),絕對不能硬編碼在代碼中,否則密鑰泄露會導致令牌被偽造!
- 若身份驗證通過,構建 JWT 的
- 返回令牌給前端:
- 將生成的 JWT 令牌放在響應體中(如
{ "code": 200, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "msg": "登錄成功" }
),或放在響應頭的Authorization
字段(格式通常為Bearer <token>
); - 返回 200(OK)狀態碼,完成登錄響應。
- 將生成的 JWT 令牌放在響應體中(如
階段 2:令牌存儲與攜帶階段(前端持久化令牌,后續請求攜帶)
登錄成功后,前端需持久化存儲令牌(避免頁面刷新后令牌丟失),并在后續所有需要身份驗證的接口請求中主動攜帶令牌,讓后端識別用戶身份。
2.1 前端做什么?
核心動作:持久化存儲令牌 → 攔截請求并攜帶令牌 → 處理令牌過期
-
持久化存儲令牌:
- 常用存儲方案對比(需根據項目需求選擇):
存儲方式 優點 缺點 適用場景 localStorage 永久存儲(除非手動刪除)、容量大(5MB) 易受 XSS 攻擊(腳本可讀取) 非敏感場景,如普通網站 sessionStorage 會話級存儲(關閉標簽頁后刪除) 易受 XSS 攻擊,頁面刷新不丟失 臨時登錄,如敏感操作頁面 Cookie 可設置 HttpOnly
(防 XSS)、Secure
(僅 HTTPS)易受 CSRF 攻擊,容量小(4KB) 高安全場景,如金融類應用 - 最佳實踐:若用 Cookie 存儲,需開啟
HttpOnly
(禁止前端 JS 讀取,防 XSS)、Secure
(僅 HTTPS 傳輸)、SameSite=Strict
(防 CSRF);若用 localStorage,需配合前端 XSS 防護(如輸入過濾、CSP 策略)。
- 常用存儲方案對比(需根據項目需求選擇):
-
攔截請求并攜帶令牌:
- 用前端請求庫(如 Axios、Fetch)的請求攔截器,對所有需要身份驗證的接口(如
/api/user/info
、/api/order/list
)自動添加令牌; - 攜帶格式:遵循后端約定,通常放在請求頭的
Authorization
字段,格式為Bearer <token>
(注意Bearer
后有空格);
示例 Axios 攔截器代碼:// Axios 請求攔截器 axios.interceptors.request.use(config => {// 從 localStorage 中獲取令牌const token = localStorage.getItem('token');if (token) {// 給請求頭添加 Authorization 字段config.headers.Authorization = `Bearer ${token}`;}return config; }, error => Promise.reject(error));
- 用前端請求庫(如 Axios、Fetch)的請求攔截器,對所有需要身份驗證的接口(如
-
處理令牌過期/無效:
- 若后端返回 401(令牌過期)或 403(令牌無效),前端需清除本地存儲的令牌,跳轉至登錄頁,并提示“登錄已過期,請重新登錄”;
- 進階方案:可使用“雙令牌機制”(Access Token + Refresh Token),Access Token 過期時,用 Refresh Token 自動請求新的 Access Token,避免頻繁讓用戶重新登錄。
2.2 后端做什么?
此階段后端無主動操作,但需為后續“令牌驗證”做準備:
- 確保密鑰和簽名算法的一致性(生成令牌和驗證令牌必須用同一套密鑰和算法);
- 若用“雙令牌機制”,需存儲 Refresh Token 的狀態(如是否已過期、是否已注銷),通常存在 Redis 中(便于快速查詢和失效)。
階段 3:接口訪問驗證階段(后端驗證令牌,確認用戶身份)
當前端攜帶 JWT 訪問需要身份驗證的接口時,后端需通過一系列校驗確認令牌合法性,進而允許/拒絕接口訪問。
3.1 前端做什么?
核心動作:發送攜帶令牌的請求 → 處理后端驗證結果
- 無需額外操作,只需通過請求攔截器自動攜帶令牌(如階段 2 所述);
- 接收后端響應:若驗證通過,正常處理接口返回的數據(如渲染用戶信息、訂單列表);若驗證失敗(401/403),執行“令牌過期處理”(如跳轉登錄頁)。
3.2 后端做什么?
核心動作:提取令牌 → 驗證令牌合法性 → 解析用戶信息 → 權限控制
-
提取請求中的令牌:
- 從請求頭的
Authorization
字段中提取令牌(需先去除Bearer
前綴); - 若未提取到令牌,直接返回 401 錯誤,提示“請先登錄”。
- 從請求頭的
-
驗證令牌合法性(核心步驟):
后端需通過 3 重校驗確保令牌有效,缺一不可:- 校驗簽名(防篡改):
用生成令牌時的密鑰和算法,對令牌的Header
+.
+Payload
重新計算簽名,與令牌的Signature
部分對比;若不一致,說明令牌被篡改,返回 403(Forbidden)錯誤。 - 校驗過期時間(防永久有效):
解碼令牌的Payload
,獲取exp
(過期時間,時間戳格式),與當前服務器時間對比;若exp < 當前時間
,說明令牌已過期,返回 401 錯誤。 - 校驗令牌有效性(防注銷/黑名單):
若項目中存在“主動注銷”功能(如用戶點擊“退出登錄”),后端需將已注銷的令牌加入“黑名單”(通常存在 Redis 中,過期時間與令牌exp
一致);校驗時需查詢黑名單,若令牌在黑名單中,返回 401 錯誤。
- 校驗簽名(防篡改):
-
解析用戶信息并綁定上下文:
- 若令牌驗證通過,解碼
Payload
中的userID
、role
等信息; - 將用戶信息綁定到當前請求的“上下文”中(如 Java 的
ThreadLocal
、Node.js 的req.user
),方便后續接口邏輯直接使用(如查詢“當前用戶的訂單”時,直接從上下文獲取userID
,無需再傳參數)。
- 若令牌驗證通過,解碼
-
權限控制(可選,基于角色/權限):
- 若接口需要特定權限(如“刪除訂單”僅允許管理員操作),從請求上下文提取用戶
role
,與接口要求的權限對比; - 若權限不滿足,返回 403 錯誤,提示“無操作權限”;若滿足,允許接口繼續執行業務邏輯(如查詢數據庫、返回數據)。
- 若接口需要特定權限(如“刪除訂單”僅允許管理員操作),從請求上下文提取用戶
三、核心區別:JWT 與傳統 Session 認證的兩端分工差異
理解 JWT 流程后,可通過對比傳統 Session 認證,更清晰看到 JWT 的“無狀態”優勢:
對比維度 | JWT 認證 | 傳統 Session 認證 |
---|---|---|
后端存儲 | 無狀態(不存儲令牌,僅存密鑰) | 需存儲 Session ID(如 Redis/數據庫) |
前端存儲 | 存儲 JWT 令牌(localStorage/Cookie) | 存儲 Session ID(通常在 Cookie 中) |
跨服務/跨域 | 支持(令牌自帶身份信息,無需共享 Session) | 不支持(需共享 Session 存儲,跨域 Cookie 受限) |
后端驗證 | 驗證令牌簽名和過期時間(本地計算,速度快) | 查 Session 存儲確認 ID 有效性(IO 操作,速度慢) |
四、安全注意事項(前端 + 后端)
-
前端安全:
- 不存儲敏感信息到 Payload(如密碼、身份證號);
- 優先用 Cookie 存儲令牌并開啟
HttpOnly
、Secure
、SameSite
; - 避免在 URL 中攜帶令牌(會被日志記錄,泄露風險高)。
-
后端安全:
- 密鑰必須安全存儲(配置中心/環境變量),定期輪換;
- 令牌過期時間不宜過長(如 1-2 小時),避免長期泄露風險;
- 開啟 HTTPS 傳輸(防止令牌被中間人攔截竊取);
- 實現“令牌黑名單”機制,處理主動注銷場景。
總結:JWT 登錄流程的核心是“后端生成可信令牌,前端攜帶令牌證明身份,后端驗證令牌合法性”。前端的核心職責是“存儲和攜帶令牌”,后端的核心職責是“驗證令牌和控制權限”,二者通過 JWT 令牌實現無狀態的身份認證,解決了傳統 Session 認證的跨域、跨服務痛點。