SpringBoot无感刷新Token的实现示例

 更新时间:2025年07月21日 11:02:36   作者:星辰聊技术  
无感刷新Token避免会话过期导致数据丢失,通过后端动态续签或前端主动请求,结合双Token机制,实现身份凭证自动更新,感兴趣的可以了解一下

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

想象这样一个场景:

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

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

问题根源

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

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

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

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

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

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

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

后端实现细节

依赖配置(pom.xml)

<dependencies>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.33</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>

JWT 工具类JwtUtil.java

 代码路径:/src/main/java/com/demo/auth/utils/JwtUtil.java

package com.demo.auth.utils;


import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;


public class JwtUtil {
    public static final long JWT_TTL = 1000L * 60 * 60 * 24; // 24小时
    public static final String JWT_KEY = "qx";


    public static String createJWT(String subject) {
        return getJwtBuilder(subject, null, UUID.randomUUID().toString().replace("-", "")).compact();
    }


    public static String createJWT(String subject, Long ttlMillis) {
        return getJwtBuilder(subject, ttlMillis, UUID.randomUUID().toString()).compact();
    }


    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        long nowMillis = System.currentTimeMillis();
        long expMillis = (ttlMillis != null ? nowMillis + ttlMillis : nowMillis + JWT_TTL);
        SecretKey secretKey = generalKey();
        return Jwts.builder()
                .setId(uuid)
                .setSubject(subject)
                .setIssuer("icoderoad")
                .setIssuedAt(new Date(nowMillis))
                .setExpiration(new Date(expMillis))
                .signWith(SignatureAlgorithm.HS256, secretKey);
    }


    public static Claims parseJWT(String jwt) throws Exception {
        return Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(jwt)
                .getBody();
    }


    public static SecretKey generalKey() {
        byte[] key = Base64.getDecoder().decode(JWT_KEY);
        return new SecretKeySpec(key, 0, key.length, "AES");
    }


    public static Date getExpiration(String jwt) {
        try {
            return parseJWT(jwt).getExpiration();
        } catch (Exception e) {
            throw new RuntimeException("Token 解析失败", e);
        }
    }
}

Token 拦截与续签逻辑

 拦截器路径:/src/main/java/com/demo/auth/interceptor/AuthInterceptor.java

public class AuthInterceptor implements HandlerInterceptor {


    private static final long REFRESH_THRESHOLD = 1000L * 60 * 5; // 剩余5分钟内刷新


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {


        String token = request.getHeader("Authorization");
        if (StringUtils.isEmpty(token)) {
            throw new RuntimeException("未登录");
        }


        Claims claims = JwtUtil.parseJWT(token);
        long now = System.currentTimeMillis();
        long exp = claims.getExpiration().getTime();


        if (exp - now < REFRESH_THRESHOLD) {
            String newToken = JwtUtil.createJWT(claims.getSubject());
            response.setHeader("X-Token-Refresh", newToken);
        }


        return true;
    }
}

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

前端拦截代码:

axios.interceptors.response.use(response => {
    const newToken = response.headers['x-token-refresh'];
    if (newToken && newToken !== localStorage.getItem('access_token')) {
        localStorage.setItem('access_token', newToken);
    }
    return response;
}, error => {
    // 处理401
    if (error.response.status === 401) {
        // 可以保存草稿后跳转登录
    }
    return Promise.reject(error);
});

关于 AccessToken 和 RefreshToken 的机制说明

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

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

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

场景问题:

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

推荐方案:

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

总结

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

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

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

相关文章

  • Mybatis Generator具体使用小技巧

    Mybatis Generator具体使用小技巧

    本文主要介绍了Mybatis Generator具体使用小技巧,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • 基于Graphics2D drawImage图片失真的解决方案

    基于Graphics2D drawImage图片失真的解决方案

    这篇文章主要介绍了基于Graphics2D drawImage图片失真的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java三种获取redis的连接及redis_String类型演示(适合新手)

    Java三种获取redis的连接及redis_String类型演示(适合新手)

    这篇文章主要介绍了Java三种获取redis的连接及redis_String类型演示(适合新手),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • SpringBoot快速过滤出一次请求的所有日志的示例代码

    SpringBoot快速过滤出一次请求的所有日志的示例代码

    在现网出现故障时,我们经常需要获取一次请求流程里的所有日志进行定位,本文给大家介绍了SpringBoot如何快速过滤出一次请求的所有日志,文中有相关的代码和示例供大家参考,需要的朋友可以参考下
    2024-03-03
  • java语言如何生成plist下载ipa文件

    java语言如何生成plist下载ipa文件

    这篇文章主要介绍了java语言如何生成plist下载ipa文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • mybatis plus配置自动create_time和update_time方式

    mybatis plus配置自动create_time和update_time方式

    在处理数据时,注意时间类型的转换非常重要,不同编程环境和数据库对时间数据的处理方式各异,因此在数据迁移或日常处理中需谨慎处理时间格式,个人经验表明,了解常用的时间转换函数和方法能有效避免错误,提高工作效率,希望这些经验能为大家带来帮助
    2024-09-09
  • Java使用JSONObject操作json实例解析

    Java使用JSONObject操作json实例解析

    这篇文章主要介绍了Java使用JSONObject操作json,结合实例形式较为详细的分析了Java使用JSONObject解析json数据相关原理、使用技巧与操作注意事项,需要的朋友可以参考下
    2020-04-04
  • jasypt-spring-boot-starter实现加解密和数据返显方式

    jasypt-spring-boot-starter实现加解密和数据返显方式

    这篇文章主要介绍了jasypt-spring-boot-starter实现加解密和数据返显方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • Spring中@PropertySource注解使用场景解析

    Spring中@PropertySource注解使用场景解析

    这篇文章主要介绍了Spring中@PropertySource注解使用场景解析,@PropertySource注解就是Spring中提供的一个可以加载配置文件的注解,并且可以将配置文件中的内容存放到Spring的环境变量中,需要的朋友可以参考下
    2023-11-11
  • java持久层框架mybatis防止sql注入的方法

    java持久层框架mybatis防止sql注入的方法

    下面小编就为大家带来一篇java持久层框架mybatis防止sql注入的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10

最新评论