为什么需要 JWT
在前后端分离架构中,后端通常只提供 RESTful API,不再依赖 Session 和 Cookie。JWT(JSON Web Token)是一种无状态的令牌方案,服务端签发后,客户端在每次请求时通过 Authorization: Bearer <token> 携带,服务端只需验证签名即可确认用户身份,无需存储会话。
JWT 的结构
JWT 由三部分组成,用点(.)分隔:
- Header:声明令牌类型和签名算法,例如
{"alg":"HS256","typ":"JWT"}。 - Payload:存放声明(claims),如用户 ID、过期时间
exp、签发时间iat等。注意 Payload 只是 Base64URL 编码,并非加密,不要放敏感信息。 - Signature:对
header.payload使用密钥和算法计算出的签名,用于防篡改。
一个典型的 JWT 形如 xxxxx.yyyyy.zzzzz。
签名:如何保证不可篡改
以 HMAC-SHA256(HS256)为例,签名过程为:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
服务端收到令牌后,用相同密钥和算法重新计算签名,若与令牌中的签名一致,则内容未被篡改。使用非对称算法(如 RS256)时,服务端用私钥签名、公钥验签,更适合多服务场景。
关键实践:
- 密钥必须足够复杂,并通过环境变量注入,不要硬编码在代码中。
- 始终校验
alg头,拒绝none算法,防止算法混淆攻击。 - 验证
exp、nbf、iss、aud等标准声明。
过期机制:为什么不能永久有效
JWT 一旦签发,在过期前始终有效,服务端无法主动撤销(除非引入黑名单)。因此必须设置合理的过期时间:
- Access Token:短期有效,通常 15 分钟到 2 小时,用于访问业务接口。
- Refresh Token:长期有效,通常 7 到 30 天,仅用于换取新的 Access Token。
exp 是 NumericDate 格式(Unix 时间戳秒数),服务端在验签时自动检查。
刷新机制:平衡安全与体验
当 Access Token 过期后,客户端不应直接跳转登录,而是用 Refresh Token 调用刷新接口获取新的 Access Token。典型流程:
- 客户端请求业务接口,收到 401 且错误码表示 token 过期。
- 客户端用 Refresh Token 调用
POST /auth/refresh。 - 服务端验证 Refresh Token 有效后,签发新的 Access Token(可选同时轮换 Refresh Token)。
- 客户端重试原请求。
安全建议:
- Refresh Token 应存储在 HttpOnly Cookie 中,避免 XSS 窃取;若存储在 localStorage,需防范 XSS。
- 服务端应记录 Refresh Token 的 jti(唯一标识),支持主动吊销。
- 刷新时检测 Token 重用:若一个 Refresh Token 被多次使用,可能已泄露,应吊销整个令牌家族。
前后端落地要点
- 前端:统一封装请求库,在拦截器中自动附加 Access Token,并处理 401 刷新逻辑,避免并发刷新导致多次请求。
- 后端:使用成熟的 JWT 库(如 Java 的 jjwt、Node.js 的 jsonwebtoken、Python 的 PyJWT)进行签发和验证,不要自行实现签名算法。
- 传输:全程使用 HTTPS,防止令牌被中间人截获。
- 存储:Access Token 可存内存或 sessionStorage,Refresh Token 优先 HttpOnly Cookie。
总结
JWT 通过签名保证完整性,通过过期时间限制风险,通过刷新机制提升用户体验。在前后端分离项目中,合理设置有效期、安全存储令牌、实现可靠的刷新流程,是构建认证体系的核心。