要理解 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 认证的跨域、跨服务痛点。