Java如何做到无感知刷新token含示例代码(值得珍藏)

1. 前言

在系统页面进行业务操作时,有时会突然遇到应用闪退,并被重定向至登录页面,要求重新登录。此问题的出现,通常与系统中用于存储用户ID和token信息的Redis缓存有关。具体来说,这可能是由于token过期所导致的身份验证失效。

要解决这个问题,可以采用两种策略:一是自动刷新token,二是token续约。通过在验证用户权限的同时为用户生成新的token并返回给客户端,可以确保客户端及时更新本地存储的token。此外,设置定时任务来刷新token也是一个有效的方法。这样,即使在token即将过期的情况下,也可以通过增加其有效时间来避免应用闪退的问题。

2. 自动刷新token

自动刷新token是一种后端解决方案,旨在解决token过期问题。后端会检查每个token的过期时间,一旦发现某个token即将过期,就会在请求头中添加一个新的token。前端在接收到请求时,会拦截该请求并从请求头中获取新的token。如果新旧token不一致,前端会直接更新本地的token,从而确保后续请求能够正常进行。这种方法可以有效避免因token过期而导致的系统闪退问题,提高了系统的稳定性和安全性。

2.1 后端实现方案

2.1.1 先引入依赖

xml 复制代码
<dependencies>  
    <!-- Java JWT -->  
    <dependency>  
        <groupId>io.jsonwebtoken</groupId>  
        <artifactId>jjwt-api</artifactId>  
        <version>0.11.2</version> <!-- 使用最新版本 -->  
    </dependency>  
    <dependency>  
        <groupId>io.jsonwebtoken</groupId>  
        <artifactId>jjwt-impl</artifactId>  
        <version>0.11.2</version> <!-- 使用最新版本 -->  
        <scope>runtime</scope>  
    </dependency>  
    <dependency>  
        <groupId>io.jsonwebtoken</groupId>  
        <artifactId>jjwt-jackson</artifactId>  
        <version>0.11.2</version> <!-- 使用最新版本 -->  
        <optional>true</optional>  
    </dependency>  
    <!-- 其他必要的依赖项 -->  
</dependencies>

2.1.2 生成token的代码

java 复制代码
import java.nio.charset.StandardCharsets;  
import java.util.Base64;  
import java.util.Date;  
  
import io.jsonwebtoken.Claims;  
import io.jsonwebtoken.Jwts;  
import io.jsonwebtoken.SignatureAlgorithm;  
import io.jsonwebtoken.security.Keys;  
  
public class TokenGenerator {  
      
    // 用于生成Token的密钥  
    private static final String SECRET = "your_secret_key"; // 请使用一个安全的密钥  
      
    /**  
     * 生成一个新的Token。  
     *   
     * @param userId 用户ID  
     * @return 生成的Token字符串  
     */  
    public static String generateToken(String userId) {  
        // 设置Token的有效期为1小时(3600秒)  
        long expirationTime = 3600L;  
          
        // 创建Token的主体部分,包括用户ID和过期时间戳  
        String tokenBody = userId + "|" + System.currentTimeMillis() + "|" + expirationTime;  
          
        // 使用Base64对Token的主体部分进行编码,以便在Token中传输  
        String encodedBody = Base64.getEncoder().encodeToString(tokenBody.getBytes(StandardCharsets.UTF_8));  
          
        // 创建Token的头部部分,包括签名算法和密钥标识符(通常为密钥的SHA-256摘要)  
        String header = "{\"alg\":\"" + SignatureAlgorithm.HS256 + "\",\"typ\":\"JWT\"}";  
        String encodedHeader = Base64.getEncoder().encodeToString(header.getBytes(StandardCharsets.UTF_8));  
          
        // 使用密钥对Token的头部和主体部分进行签名,生成最终的Token  
        String token = Jwts.builder()  
                           .setHeader(encodedHeader) // 设置头部信息  
                           .setClaims(Claims.builder().subject(encodedBody).expiration(new Date(System.currentTimeMillis() + expirationTime * 1000)).build()) // 设置主体信息和过期时间戳  
                           .signWith(Keys.hmacShaKeyFor(SECRET)) // 使用HMAC SHA-256算法和密钥进行签名  
                           .compact(); // 生成最终的Token字符串  
          
        return token; // 返回生成的Token字符串  
    }  
}

2.1.3 测试生成token

java 复制代码
import java.util.Base64;  
  
public class TokenGeneratorExample {  
    public static void main(String[] args) {  
        String userId = "12345"; // 替换为实际的用户ID  
        String token = TokenGenerator.generateToken(userId);  
        System.out.println("Generated Token: " + token);  
    }  
}

2.2 前端续约token

token续约更倾向于采用前端解决方案,即由前端来处理token的过期时间。首先,前端和后端需要协商并确定一个用于token续约的接口。当前端检测到某个token即将过期时,它会向后端发送该token,然后由后端来延长该token的过期时间。

在前端实现方案中,使用的是双Token方式,即access-token(AT)和refresh-token(RT)。而对于纯后端方式,则只使用access-token。

那么,AT和RT之间有什么区别?为什么需要RT?实际上,AT的暴露机会更多,因为每个请求都需要携带它。为了降低被劫持的风险,AT的过期时间通常设置得较短。而RT仅在auth服务中用于刷新AT,因此它的过期时间通常设置得较长,以增加便利性。

AT和RT的设计是为了提高网络传输的安全性。在网络传输过程中,AT容易暴露,因为它的过期时间较短。通过使用AT和RT,可以降低这种风险。这种设计已经成为了一种标准的安全处理方式,就像https之于http一样,无需再探讨其合理性。

2.3 疑问及思考

当用户在前端表单页面填写内容时,如果长时间没有进行请求发送,导致Token过期,后端返回401错误,这是一个常见的问题。下面是对这个问题的重写:

如果前端有一个表单页面,用户在长时间没有发送请求的情况下填写了表单,然后尝试提交,但后端返回了401错误(表示未授权或Token已过期),该如何解决?

对于这种情况,解决方案可以考虑以下几点:

  • 对于纯后端解决方案,一种可能的做法是让前端在表单填写过程中进行特殊处理。如果提交表单后返回401错误,前端可以将表单数据存储在本地存储中,然后跳转到登录页面。用户登录成功后,页面将返回表单页面,并从本地存储中取出之前保存的数据,重新填充到表单中。

  • 对于前端解决方案,当Token过期时(无论是access-token还是refresh-token),可以采取以下措施:

    • 监听refresh-token的过期时间,当它接近过期时,向后端发起请求来刷新refresh-token。这样可以确保在Token过期之前进行刷新,避免提交表单时出现401错误。

    • 实现类似草稿箱的功能,将表单数据保存在前端本地存储中。当检测到Token过期时,可以提示用户重新登录,并在登录成功后恢复之前保存的数据。这样即使Token过期,用户也可以重新登录并继续之前未完成的操作。

    • 以上解决方案可以帮助解决Token过期导致的问题,提高用户的体验和系统的安全性。

相关推荐
秃头佛爷1 分钟前
Python学习大纲总结及注意事项
开发语言·python·学习
阿伟*rui2 分钟前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
待磨的钝刨3 分钟前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei2 小时前
java的类加载机制的学习
java·学习
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer4 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq4 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端