2. JWT 工作原理:签发流程、验证流程、Token 的存储方式

JWT(JSON Web Token)是目前分布式系统中最常用的无状态认证方案。理解其签发与验证流程,以及 Token 的客户端存储策略,是构建安全异步任务系统的前提。本章从原理到实践,拆解 JWT 的核心机制。

2.1 JWT 的结构与组成

一个 JWT 由三个 Base64URL 编码的部分组成,以点号(.)分隔:

header.payload.signature
部分 内容 示例
Header 令牌类型(typ)与签名算法(alg) {"alg":"HS256","typ":"JWT"}
Payload 声明(Claims):用户身份、权限、过期时间等 {"sub":"user_123","role":"admin","exp":1718000000}
Signature 对前两部分使用密钥生成的签名,防篡改 HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload), secret)
关键点:Payload 仅做 Base64URL 编码,不是加密。敏感信息(如密码、身份证号)严禁放入 JWT。签名只保证数据未被篡改,不保证内容不可见。

2.2 JWT 签发流程

签发流程发生在用户登录成功时,服务端生成 Token 并返回给客户端。典型步骤如下:

  1. 用户提交凭证:客户端发送用户名/密码到认证接口(如 /api/login)。
  2. 服务端验证身份:查询数据库,比对密码哈希。验证失败则返回 401。
  3. 构建 Payload:将用户 ID、角色、权限范围、签发时间(iat)、过期时间(exp)等放入 Claims。
  4. 生成签名:使用服务端密钥(Secret Key)对 Header + Payload 进行签名。
  5. 返回 Token:将完整的 JWT 字符串返回给客户端。
安全实践:密钥必须存储在服务端环境变量或密钥管理服务中,绝不可硬编码在代码或暴露给前端。推荐使用 RS256(非对称算法),私钥签名,公钥验证,避免密钥泄露风险。
JWT 签发流程 客户端 提交凭证 认证服务 验证身份 构建 Payload sub/role/exp 生成签名 密钥 + 算法 客户端 存储 Token

2.3 JWT 验证流程

当客户端发起需要认证的请求时,服务端对 Token 进行验证。验证过程不依赖数据库会话,这是 JWT 无状态特性的核心。

  1. 提取 Token:从请求头 Authorization: Bearer <token> 中获取 JWT。
  2. 解析 Header:解码 Base64URL,获取签名算法(alg)。
  3. 验证签名:使用服务端密钥(或公钥)重新计算签名,与 Token 中的签名比对。不一致则拒绝。
  4. 检查有效期:验证 exp(过期时间)是否在当前时间之后。过期则返回 401。
  5. 提取 Claims:验证通过后,从 Payload 中读取用户身份与权限,供后续业务逻辑使用。
常见漏洞:攻击者可能将 alg 改为 none 绕过签名验证。服务端必须强制校验算法白名单,拒绝 none 算法。
JWT 验证流程 客户端请求 携带 Bearer Token 提取 Token 解析 Header 验证签名 密钥比对 检查有效期 exp 是否过期 提取 Claims 用户身份/权限

2.4 Token 的存储方式:LocalStorage vs Cookie

客户端收到 JWT 后,需要选择存储位置。两种主流方案各有优劣,直接影响安全性。

对比维度 LocalStorage Cookie(HttpOnly + Secure)
访问方式 JavaScript 可直接读取(localStorage.getItem) 仅由浏览器自动发送,JS 无法读取(HttpOnly)
XSS 风险 高:攻击者通过 XSS 脚本可窃取 Token 低:HttpOnly 属性阻止 JS 访问
CSRF 风险 低:Token 不在请求中自动携带 高:Cookie 自动附加,需额外 CSRF Token 防护
跨域支持 需手动设置 Authorization 头,灵活 需配置 SameSite 与 CORS,较复杂
持久性 持久存储,需手动清除 可设置过期时间,支持 Session Cookie
推荐场景 SPA 应用 + 强 XSS 防护 + 短生命周期 Token 传统 Web 应用 + CSRF 防护 + 敏感操作
最佳实践:对于异步任务系统,推荐使用 Cookie + HttpOnly + Secure + SameSite=Strict 存储 Refresh Token,而 Access Token 可放在内存变量中(非持久化),降低 XSS 与 CSRF 双重风险。

2.5 存储方式决策树

在实际项目中,可根据以下条件快速选择存储策略:

  • 是否完全控制前端代码? 是 → 考虑 Cookie;否 → 考虑 LocalStorage。
  • 是否担心 XSS 攻击? 是 → 优先 Cookie(HttpOnly)。
  • 是否使用第三方 API 或跨域? 是 → LocalStorage 更灵活。
  • 是否需要无状态认证? 是 → 两种均可,但 Cookie 需配合 CSRF 防护。
安全底线:无论选择哪种存储方式,永远不要在 URL 参数、日志、错误信息中泄露 JWT。Token 的生命周期应尽量短(建议 Access Token 15 分钟,Refresh Token 7 天),并配合黑名单机制处理登出场景。