JWT Token 鉴权流程
标准三阶段流程
登录(签发):用户输入账号密码,服务端验证通过后,生成一个包含Header(算法)、Payload(用户ID/角色)和Signature(防篡改签名)的JWT字符串,返回给前端。
携带(传输):前端通常将其存储在LocalStorage或Cookie(HttpOnly)中,并在后续请求的Authorization: Bearer 头中携带。
校验(拦截):服务端拦截器解析Token,验签(确保未被篡改),并从中提取用户信息存入ThreadLocal或Context,供本次请求后续使用。
状态本质
JWT是“自包含”的。服务端不需要查询数据库来验证Token是否有效(因为签名算法保证了数据的完整性),这是它区别于Session的核心优势(无状态、水平扩展友好)。
安全细节
存储安全:如果是敏感系统,不存LocalStorage(易受XSS攻击),放在HttpOnly Cookie中并配合SameSite=Strict,虽然防不了CSRF但能防XSS窃取。”
传输安全:必须强调仅限HTTPS传输,防止中间人攻击。
敏感信息:Payload里绝对不存密码、银行卡等敏感信息,因为JWT是Base64编码,等于明文。
JWT Token 过期失效了怎么办?
引入 Refresh Token(刷新令牌)机制 - 双Token设计:登录时返回两个Token。
- **Access Token**:有效期短(如15-30分钟),用于日常请求。
- **Refresh Token**:有效期长(如7天或30天),存储在服务端(或HttpOnly Cookie)。
-
自动续期流程:
- 请求携带Access Token,发现过期(返回特定错误码,如
code: 401)。 - 前端拦截器捕捉该错误,不跳转登录,而是静默调用
/refresh接口,携带Refresh Token。 - 服务端验证Refresh Token有效,签发新的Access Token返回前端。
- 前端重试刚才失败的请求。
- 请求携带Access Token,发现过期(返回特定错误码,如
-
效果:用户无感知续签,不用频繁登录。
-
针对并发的优化(“雪花问题”) :
- 如果同时发起的10个请求都过期了,难道要发10次
/refresh请求吗? - 前端需要做请求队列(或锁机制)。第一个请求去刷新Token,其余请求进入等待队列,待新Token下发后,统一重试所有排队请求。
- 如果同时发起的10个请求都过期了,难道要发10次
-
登出与黑名单(“注销即失效”) :
- JWT是无状态的,但用户改密码或注销时,旧Token在有效期内依然能用(这是个痛点)。
- 针对核心操作(如支付、修改密码),即使Token未过期,校验Redis黑名单(Token指纹)。同时,Refresh Token一旦被使用,应立即作废(或者使用一次性的Family Token模式)。
-
阈值刷新策略:
-
不仅仅是过期才刷新。在Token剩余有效时间小于5分钟时,就主动利用Refresh Token换取新Token,而不是等到报401,这样用户体验更丝滑(零报错)。
-