当前位置: 首页 > news >正文

SpringBoot前后端token自动续期方案

1. 背景

在前后端分离架构下,用户登录成功后,后端服务会颁发一个 token 作为用户身份凭证。前端(如 Vue 应用)在接收到 token 后,通常将其存储到 LocalStorage,并在每次请求时通过请求头携带该 token 访问后端服务。

后端服务会在过滤器中对 token 进行校验,包括合法性和是否过期。当 token 已过期时,后端会返回错误信息,引导前端跳转至登录页面,要求用户重新登录。

图片

这种机制虽然简单,但在实际使用中存在一个明显的用户体验问题。例如:

  • • 用户正在填写一个较复杂的表单,提交时发现 accessToken 已过期,不得不重新登录并重新填写;

  • • 用户在持续操作系统,但由于 accessToken 有固定时效,依旧会在某一时刻被强制退出。

这类场景都会造成较差的用户体验。那么,有没有办法在用户活跃操作时,自动延长 token 的有效期呢?

本文将介绍两种常见的自动续期方案:基于前端的刷新机制 和 基于后端的自动续期机制

2. 自动续期实现方案

2.1. 基于前端的自动续期(Refresh Token)

如果系统采用 OAuth2 协议进行认证,并且支持 Refresh Token,就可以通过前端实现自动续期。

典型的认证响应如下:

{"access_token": "eyJhbGciOiJFUzI1NiIs**********X6wrZHYKDxJkWwhdkrYg","token_type": "Bearer","expires_in": 7200,"refresh_token": "eyJhbGciOiJFUzI1NiIs**********XXOYOZz1mfgIYHwM8ZJA", "refresh_token_expires_in": 604800......
}

说明:

  • • access_token:访问令牌(通常有效期 1~2 小时),前端调用后端接口时使用。

  • • refresh_token:刷新令牌(有效期较长,常见 7~30 天),用于在 access_token 过期时获取新的 access_token

此时,可以在前端通过 响应拦截器 自动处理续期逻辑:

// Axios响应拦截器实现自动续期
axios.interceptors.response.use((response) => response,async (error) => {const originalRequest = error.config;if (error.response?.status === 401 && !originalRequest._retry) {originalRequest._retry = true;try {const refreshToken = localStorage.getItem('refresh_token');const refreshResponse = await axios.post('/auth/refresh', {refresh_token: refreshToken});// 更新TokenlocalStorage.setItem('access_token', refreshResponse.data.access_token);localStorage.setItem('refresh_token', refreshResponse.data.refresh_token);// 重新发送原始请求originalRequest.headers.Authorization = `Bearer ${refreshResponse.data.access_token}`;returnaxios(originalRequest);} catch (refreshError) {// 续期失败,清除Token并跳转登录localStorage.clear();window.location.href = '/login';returnPromise.reject(refreshError);}}returnPromise.reject(error);}
);

这种方式的优点是清晰易懂,基于 OAuth2 标准实现;缺点是必须有 refresh_token 机制 支持,否则无法使用。

2.2 基于后端实现自动续期

在很多项目中,认证并未采用双 token 模式,而是只有一个 accessToken(通常为 JWT)。此时,可以在后端引入一层“间接认证”来实现自动续期。

实现思路如下:

登录时生成双 token

  • • 登录成功后生成一个 uuidToken(随机 UUID),同时生成一个 accessToken

  • • 将 uuidToken 作为 key,accessToken(及用户信息)作为 value 存入缓存,缓存过期时间为 accessToken 有效期的 2 倍;

  • • 返回 uuidToken 给前端。

注意:JWT 本身长度较长,不适合作为缓存 key,因此使用短 UUID 替代。

请求拦截与校验

  • • 前端请求时携带 uuidToken

  • • 后端通过 uuidToken 从缓存中获取 accessToken

  • • 若缓存不存在,则判定为用户长时间未操作,要求重新登录;

  • • 若 accessToken 已过期,但 uuidToken 未过期,说明用户仍在活跃操作,此时后端可为其生成新的 accessToken 并覆盖缓存,从而实现自动续期。

前端无感知

  • • 前端始终只持有 uuidToken

  • • accessToken 的变化仅在后端进行更新,对前端无影响。

登录逻辑示例

public String login(String userName, String password) {StringuuidToken= UUID.randomUUID().toString();SysUsersysUser= userService.getUserByUserName(userName);// ...认证逻辑...StringaccessToken= JwtUtil.createJWT(sysUser);LoginUserVOloginUserVO=newLoginUserVO();loginUserVO.setUserAccount(sysUser.getUserId());loginUserVO.setName(sysUser.getName());loginUserVO.setRole("Manager");loginUserVO.setAccessToken(accessToken);// 存储用户信息至缓存userTokenService.storeUserToken(uuidToken, loginUserVO);return uuidToken;
}

后端请求过滤器示例

@Slf4j
@Component
publicclassTokenFilterextendsOncePerRequestFilter {@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)throws IOException {try {StringrequestPath= request.getRequestURI();// 白名单放行if (isWhiteListPath(requestPath)) {filterChain.doFilter(request, response);return;}// 获取UUID TokenStringuuidToken= getAccessToken(request);if (uuidToken == null) {sendUnauthorizedResponse(response, "Missing token");return;}// 获取用户信息LoginUserVOloginUserVO= userTokenService.getUserByToken(uuidToken);if (loginUserVO == null) {sendUnauthorizedResponse(response, "Token expired or invalid");return;}// 检查并续期loginUserVO = userTokenService.checkAndRefreshIfNeeded(uuidToken, loginUserVO);// 设置用户上下文UserContextHolder.setContext(UserContext.fromUserToken(uuidToken, loginUserVO));filterChain.doFilter(request, response);} finally {UserContextHolder.clearContext();}}private String getAccessToken(HttpServletRequest request) {Stringtoken= request.getHeader("Authorization");return (token != null) ? token : request.getParameter("Authorization");}
}

3. 总结

在前后端分离的应用中,token 的续期机制直接影响用户体验。

  • • 基于前端的方案(Refresh Token)适用于标准 OAuth2 认证体系,方案清晰,但依赖协议支持;

  • • 基于后端的方案(UUID Token + 自动刷新)则适用于只有单一 accessToken 的场景,能在后端无感知地为用户自动续期。

在实际项目中,应根据系统架构和认证机制选择合适的续期方案,从而在安全性和用户体验之间取得平衡。

http://www.lryc.cn/news/626347.html

相关文章:

  • nginx-下载功能-状态统计-访问控制
  • 开源的实时 Web 日志分析器GoAccess安装使用指南
  • 鸿蒙安卓前端中加载丢帧:ArkWeb分析
  • Vue3全局配置Loading的完整指南:从基础到实战
  • electron进程间通信-从主进程到渲染器进程
  • PyTorch API 3 - distributed
  • 实时视频技术选型深度解析:RTSP、RTMP 与 WebRTC 的边界
  • SpringWeb详解
  • Causal-Copilot: An Autonomous Causal Analysis Agent 论文解读
  • Nginx 作为反向代理时设置的请求头
  • 深度解析:RESTful API中的404错误 - 不是所有404都是Bug
  • 安路EF2系列芯片单口ram ip核使用方法
  • 记录 docker容器打包成镜像 在其他服务器快速启动镜像和容器
  • 零基础学Java第二十二讲---异常(2)
  • KV cache
  • 在Excel和WPS表格中制作可打印的九九乘法表
  • MySQL事务及原理详解
  • MySQL 数据与表结构导出 Excel 技术文档
  • 如何使用matlab将目录下不同的excel表合并成一个表
  • python中view把矩阵维度降低的时候是什么一个排序顺序
  • 系统架构设计师备考第1天——系统架构概述
  • 深入Linux内核:架构设计与核心功能解析
  • 车联网(V2X)中万物的重新定义---联网汽车新时代
  • 自动驾驶汽车机器学习安全实用解决方案
  • RK android14 Setting一级菜单IR遥控器无法聚焦问题解决方法
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第二章学习笔记及总结)
  • 汽车ECU实现数据安全存储(机密性保护)的一种方案
  • 【openssl】openssl CA.pl 签发证书操作步骤
  • Redis String全方位指南:命令、编码、时间复杂度与应用场景
  • RK-Android11-PackageInstaller安装器自动安装功能实现