设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间20 现在时间20 缓存数据 和聪明人交流,和靠谱的人恋爱,和进取的人共事,和幽默的人随行。晚安!

和聪明人交流,和靠谱的人恋爱,和进取的人共事,和幽默的人随行。晚安!

查看: 652|回复: 0

SpringBoot无感刷新Token的实现示例

[复制链接]

  离线 

TA的专栏

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 打卡月天数:0
  • 打卡总奖励:59
  • 最近打卡:2023-08-16 05:45:13
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
36
主题
30
精华
0
金钱
166
积分
68
注册时间
2023-8-12
最后登录
2025-8-28

发表于 2025-8-28 00:32:49 | 显示全部楼层 |阅读模式

背景问题:为什么需要无感刷新?

想象这样一个场景:

“我正在后台管理系统中录入数据,页面突然跳转回登录界面,之前填写的内容全没了!”

这是典型的 Token 到期导致会话失效 的问题,尤其在使用 Redis 等缓存中间件存储 Token 时尤为常见。

问题根源

后端通常通过 JWT 来实现无状态身份验证,但 JWT 的缺陷也很明显:过期即失效,无法修改或撤销。如果不设计 Token 刷新机制,用户体验将大打折扣。

核心策略:Token 无感续签方案概述

方案一:后端自动续期(推荐)

在每次用户请求时,后端检查当前 Token 的有效时间:

  • 若临近过期(如小于5分钟) ,则动态生成一个新 Token,加入响应头中返回;
  • 前端拦截响应头,若发现新的 Token,与本地不一致则自动更新本地 Token。

方案二:前端主动续签(补充方案)

  • 前端维护一对 Token:
    1. access_token
    复制代码
    (短期)+ 
    1. refresh_token
    复制代码
    (长期);
  • 每隔一段时间,前端使用 
    1. refresh_token
    复制代码
     去调用刷新接口,获取新的 
    1. access_token
    复制代码

后端实现细节

依赖配置(pom.xml)

  1. <dependencies>
  2. <dependency>
  3. <groupId>cn.hutool</groupId>
  4. <artifactId>hutool-all</artifactId>
  5. <version>5.5.1</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.alibaba</groupId>
  9. <artifactId>fastjson</artifactId>
  10. <version>1.2.33</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>io.jsonwebtoken</groupId>
  14. <artifactId>jjwt</artifactId>
  15. <version>0.9.1</version>
  16. </dependency>
  17. </dependencies>
复制代码

JWT 工具类JwtUtil.java

 代码路径:

  1. /src/main/java/com/demo/auth/utils/JwtUtil.java
复制代码

  1. package com.demo.auth.utils;
  2. import io.jsonwebtoken.*;
  3. import javax.crypto.SecretKey;
  4. import javax.crypto.spec.SecretKeySpec;
  5. import java.util.*;
  6. public class JwtUtil {
  7. public static final long JWT_TTL = 1000L * 60 * 60 * 24; // 24小时
  8. public static final String JWT_KEY = "qx";
  9. public static String createJWT(String subject) {
  10. return getJwtBuilder(subject, null, UUID.randomUUID().toString().replace("-", "")).compact();
  11. }
  12. public static String createJWT(String subject, Long ttlMillis) {
  13. return getJwtBuilder(subject, ttlMillis, UUID.randomUUID().toString()).compact();
  14. }
  15. private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
  16. long nowMillis = System.currentTimeMillis();
  17. long expMillis = (ttlMillis != null ? nowMillis + ttlMillis : nowMillis + JWT_TTL);
  18. SecretKey secretKey = generalKey();
  19. return Jwts.builder()
  20. .setId(uuid)
  21. .setSubject(subject)
  22. .setIssuer("icoderoad")
  23. .setIssuedAt(new Date(nowMillis))
  24. .setExpiration(new Date(expMillis))
  25. .signWith(SignatureAlgorithm.HS256, secretKey);
  26. }
  27. public static Claims parseJWT(String jwt) throws Exception {
  28. return Jwts.parser()
  29. .setSigningKey(generalKey())
  30. .parseClaimsJws(jwt)
  31. .getBody();
  32. }
  33. public static SecretKey generalKey() {
  34. byte[] key = Base64.getDecoder().decode(JWT_KEY);
  35. return new SecretKeySpec(key, 0, key.length, "AES");
  36. }
  37. public static Date getExpiration(String jwt) {
  38. try {
  39. return parseJWT(jwt).getExpiration();
  40. } catch (Exception e) {
  41. throw new RuntimeException("Token 解析失败", e);
  42. }
  43. }
  44. }
复制代码

Token 拦截与续签逻辑

 拦截器路径:

  1. /src/main/java/com/demo/auth/interceptor/AuthInterceptor.java
复制代码

  1. public class AuthInterceptor implements HandlerInterceptor {
  2. private static final long REFRESH_THRESHOLD = 1000L * 60 * 5; // 剩余5分钟内刷新
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  5. String token = request.getHeader("Authorization");
  6. if (StringUtils.isEmpty(token)) {
  7. throw new RuntimeException("未登录");
  8. }
  9. Claims claims = JwtUtil.parseJWT(token);
  10. long now = System.currentTimeMillis();
  11. long exp = claims.getExpiration().getTime();
  12. if (exp - now < REFRESH_THRESHOLD) {
  13. String newToken = JwtUtil.createJWT(claims.getSubject());
  14. response.setHeader("X-Token-Refresh", newToken);
  15. }
  16. return true;
  17. }
  18. }
复制代码

前端处理逻辑(以 Vue + Axios 为例)

前端拦截代码:

  1. axios.interceptors.response.use(response => {
  2. const newToken = response.headers['x-token-refresh'];
  3. if (newToken && newToken !== localStorage.getItem('access_token')) {
  4. localStorage.setItem('access_token', newToken);
  5. }
  6. return response;
  7. }, error => {
  8. // 处理401
  9. if (error.response.status === 401) {
  10. // 可以保存草稿后跳转登录
  11. }
  12. return Promise.reject(error);
  13. });
复制代码

关于 AccessToken 和 RefreshToken 的机制说明

类型用途特点
AccessToken携带用户身份,频繁使用安全风险高,需短时过期
RefreshToken用于续签 AccessToken不暴露给前端,一般保存在 Cookie 或 HttpOnly

标准双 Token 模式提升了安全性和用户体验,避免因 AccessToken 频繁刷新带来的资源浪费。

特别讨论:表单静默超时的处理策略

场景问题:

用户长时间填写表单,没有发出任何请求,点击提交时发现 token 已失效,被重定向到登录页,数据全丢。

推荐方案:

  • 提交失败后前端本地缓存表单数据;
  • 登录成功后回显草稿,确保用户体验不受损;
  • 或者在用户输入行为时定期心跳请求,触发后端续签。

总结

实现无感刷新 Token,是用户体验与安全性协同优化的重要实践。通过后端智能判断与前端拦截配合,结合双 Token 模式或动态续签机制,我们可以实现:

用户操作不中断 身份凭证自动续期 安全控制粒度更灵活

到此这篇关于SpringBoot无感刷新Token的实现示例的文章就介绍到这了,更多相关SpringBoot无感刷新Token内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表