news 2026/6/26 9:41:00

JWT + Refresh Token 双 Token 方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JWT + Refresh Token 双 Token 方案

JWT + Refresh Token 双 Token 方案

文章目录

  • JWT + Refresh Token 双 Token 方案
    • 1. 背景
    • 2. 架构设计
      • 2.1 总体流程
      • 2.2 Token 类型对比
      • 2.3 JWT Payload 结构
    • 3. 核心实现
      • 3.1 配置
      • 3.2 Token 生成(JwtTokenProvider)
      • 3.3 登录返回双 Token(AuthController)
      • 3.4 Token 刷新接口
      • 3.5 前端 401 自动刷新拦截器
    • 4. 安全性分析
      • 4.1 Access Token 泄露
      • 4.2 Refresh Token 泄露
      • 4.3 type claim 验证
    • 5. 与单 Token 方案对比
    • 6. 适用场景
    • 7. 注意事项

1. 背景

传统的单 Token JWT 方案存在一个矛盾:

过期时间短过期时间长
安全性✅ Token 泄露影响小❌ Token 泄露影响大
用户体验❌ 频繁重新登录✅ 无需频繁登录

双 Token 方案通过引入Access TokenRefresh Token两个 Token,同时兼顾安全性和用户体验。


2. 架构设计

2.1 总体流程

┌─────────────────────────────────────────────────────────┐ │ 登录 │ │ 用户名密码 / 手机验证码 / OAuth2 │ └────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 签发双 Token │ │ ├─ Access Token (30分钟, 用于 API 鉴权) │ │ └─ Refresh Token (7~14天, 仅用于刷新) │ └────────────────────┬────────────────────────────────────┘ │ ┌───────────┴───────────┐ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 正常请求 │ │ Access Token │ │ 携带 Access │ │ 过期(401) │ │ 正常响应 │ │ │ └─────────────────┘ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 自动用 Refresh │ │ Token 换新 │ │ │ ├── 成功 → 重试请求 └── 失败 → 跳转登录 └─────────────────┘

2.2 Token 类型对比

属性Access TokenRefresh Token
用途API 鉴权(Authorization 头)换取新的 Access Token
有效期30 分钟7 天(默认)/ 14 天(记住我)
携带用户信息✅ 含 email、avatar 等❌ 仅含用户名
存储位置前端 localStorage前端 localStorage
服务端状态❌ 无状态❌ 无状态(JWK 验证)
传输频率每次 API 请求仅过期时
泄露影响影响 30 分钟较长,但仅能用于刷新

2.3 JWT Payload 结构

Access Token:

{"sub":"zhangsan","type":"access","email":"zhangsan@example.com","avatar":"https://...","iat":1719200000,"exp":1719201800}

Refresh Token:

{"sub":"zhangsan","type":"refresh","email":"","avatar":"","iat":1719200000,"exp":1719804800}

通过typeclaim 区分两种 Token,JwtAuthenticationFilter只认type=access的 Token。


3. 核心实现

3.1 配置

app:jwt:secret:YourSecretKeyForJWT...access-token-expiration-ms:1800000# 30 分钟refresh-token-expiration-ms:604800000# 7 天(默认)refresh-token-remember-me-expiration-ms:1209600000# 14 天(记住我)

3.2 Token 生成(JwtTokenProvider)

// 生成 Access Token(含用户信息)publicStringgenerateAccessToken(Stringusername,Stringemail,Stringavatar){returnbuildToken(username,email,avatar,accessTokenExpirationMs,"access");}// 生成 Refresh Token(根据 rememberMe 决定有效期)publicStringgenerateRefreshToken(Stringusername,booleanrememberMe){longexpiry=rememberMe?refreshTokenRememberMeExpirationMs:refreshTokenExpirationMs;returnbuildToken(username,null,null,expiry,"refresh");}

3.3 登录返回双 Token(AuthController)

@PostMapping("/login")publicResponseEntity<ApiResponse<LoginResponse>>login(@Valid@RequestBodyLoginRequestrequest){// ... 认证逻辑 ...StringaccessToken=jwtTokenProvider.generateAccessToken(username,email,avatar);StringrefreshToken=jwtTokenProvider.generateRefreshToken(username,request.isRememberMe());LoginResponseresponse=newLoginResponse(accessToken,refreshToken,username,avatar);returnResponseEntity.ok(ApiResponse.success("登录成功",response));}

3.4 Token 刷新接口

@PostMapping("/refresh")publicResponseEntity<ApiResponse<LoginResponse>>refresh(@RequestBodyMap<String,String>request){StringrefreshTokenValue=request.get("refreshToken");// 验证 Refresh Tokenif(!jwtTokenProvider.validateRefreshToken(refreshTokenValue)){returnResponseEntity.status(401).body(ApiResponse.error("refreshToken 无效或已过期"));}// 提取用户信息Stringusername=jwtTokenProvider.getUsernameFromRefreshToken(refreshTokenValue);// 签发新的双 Token(Refresh Token 轮换)StringnewAccessToken=jwtTokenProvider.generateAccessToken(username,email,avatar);StringnewRefreshToken=jwtTokenProvider.generateRefreshToken(username,false);returnResponseEntity.ok(ApiResponse.success("Token 刷新成功",response));}

3.5 前端 401 自动刷新拦截器

// Axios 响应拦截器http.interceptors.response.use((response)=>response,async(error)=>{if(error.response?.status!==401)returnPromise.reject(error)// 刷新接口本身 401 → 跳转登录if(originalRequest.url==='/api/auth/refresh'){clearAuth();window.location.href='/login'returnPromise.reject(error)}// 用 Refresh Token 换新constres=awaitaxios.post('/api/auth/refresh',{refreshToken:localStorage.getItem('refreshToken')})const{token,refreshToken}=res.data.data localStorage.setItem('token',token)localStorage.setItem('refreshToken',refreshToken)// 重试原始请求originalRequest.headers.Authorization=`Bearer${token}`returnhttp(originalRequest)})

4. 安全性分析

4.1 Access Token 泄露

  • 有效期仅 30 分钟
  • 攻击者只能在这 30 分钟内使用
  • 用户刷新 Token 后旧的 Access Token 自动失效(因为 Refresh Token 轮换,但 Access Token 本身是无状态的,需等自然过期)

4.2 Refresh Token 泄露

  • 传输频率极低(仅在过期时通过/api/auth/refresh传输一次)
  • Refresh Token 轮换:每次刷新签发新的 Refresh Token,旧的不再有效
  • 后续可升级为 Redis 存储(服务端有状态),支持手动撤销

4.3 type claim 验证

privatebooleanvalidateToken(Stringtoken,StringexpectedType){Claimsclaims=parseClaims(token);returnexpectedType.equals(claims.get("type",String.class));}
  • JwtAuthenticationFilter只验证type=access的 Token
  • Refresh Token 即使被传给 API 接口,也会因 type 不匹配而被拒绝

5. 与单 Token 方案对比

对比项单 Token双 Token(本方案)
Token 数量1 个2 个
过期时间7~14 天(统一)Access 30min + Refresh 7~14d
安全性泄露影响大泄露影响小(30 分钟窗口)
用户体验无需用户额外操作无感刷新
实现复杂度简单中等
服务端存储无(完全无状态)
Token 撤销只能等过期可通过 Refresh Token 轮换实现软撤销

6. 适用场景

  • Web 应用:Token 存储在 localStorage,Access Token 短过期 + Refresh Token 自动续期
  • 移动端:Token 存储在安全存储区,减少重新登录频率
  • 单点登录(SSO):Refresh Token 作为长期凭证,Access Token 用于各子系统鉴权

7. 注意事项

  1. 前端必须处理并发刷新:多个请求同时 401 时,只发起一次刷新请求,其他请求排队等待(见isRefreshing+pendingRequests实现)
  2. Refresh Token 也要有合理有效期:7 天默认 + 14 天记住我,不宜过长
  3. HTTPS 传输:所有 Token 必须通过 HTTPS 传输,防止中间人窃取
  4. 后续可升级点
    • Refresh Token 存入 Redis → 支持手动撤销
    • 登录设备管理 → 一个用户可撤销指定设备的 Refresh Token
    • 指纹识别 → 绑定设备信息到 Refresh Token 中
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 9:39:13

工业设备故障码深度解析:从obe-00904看编码器电池报警排查全流程

1. 项目概述&#xff1a;从“obe-00904”看一个典型工业设备故障码的深度解析如果你在工厂车间、设备维护现场或者自动化控制系统的监控屏幕上&#xff0c;看到一个像“obe-00904”这样的代码突然跳出来&#xff0c;心里会不会“咯噔”一下&#xff1f;对于一线工程师和设备维护…

作者头像 李华
网站建设 2026/6/26 9:24:49

【企业级Linux开发沙箱构建手册】:基于VMware Workstation Pro 17的隔离、快照、克隆三重保障方案

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;企业级Linux开发沙箱的核心价值与设计哲学 企业级Linux开发沙箱并非简单的隔离环境&#xff0c;而是融合安全边界、可复现性、协作一致性与运维可观测性的系统性基础设施。其设计哲学根植于“最小信任原则”与…

作者头像 李华
网站建设 2026/6/26 9:24:41

BatteryML深度解析:如何用机器学习技术破解电池寿命预测难题

BatteryML深度解析&#xff1a;如何用机器学习技术破解电池寿命预测难题 【免费下载链接】BatteryML 项目地址: https://gitcode.com/gh_mirrors/ba/BatteryML 在电动汽车、储能系统和消费电子领域&#xff0c;电池性能衰减是制约技术进步的关键瓶颈。传统基于物理模型…

作者头像 李华
网站建设 2026/6/26 9:14:59

企业如何选一人多岗处理财务运营的AI智能助手

企业如何选一人多岗处理财务运营的AI智能助手在数字化转型的过程中&#xff0c;许多企业面临着人力成本高企与流程效率低下的双重压力。对于中小型企业而言&#xff0c;寻找一款能够一人多岗处理财务和运营基础任务的智能助手推荐清单&#xff0c;往往是为了解决人手不足或重复…

作者头像 李华