JWT+redis实现令牌刷新优化方案

devtools/2025/2/28 20:44:53/

令牌刷新优化方案的详细实现步骤:

1. 令牌服务层改造

1.1 JWT工具类增强
java">// JwtUtils.java 新增方法
public class JwtUtils {// 生成带动态过期时间的令牌public static String createToken(String subject, String userId, String username, long expirationMinutes) {return Jwts.builder().setSubject(subject).claim(USER_ID, userId).claim(USERNAME, username).setExpiration(new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000)).signWith(SECRET_KEY).compact();}// 刷新令牌方法public static String refreshToken(Claims claims, long expirationMinutes) {return createToken(claims.getSubject(), claims.get(USER_ID, String.class),claims.get(USERNAME, String.class),expirationMinutes);}
}

2. 网关过滤器逻辑优化

2.1 新增阈值常量
java">// AuthFilter.java 头部添加
private static final int WARNING_THRESHOLD = 15 * 60;  // 15分钟(秒)
private static final int CRITICAL_THRESHOLD = 5 * 60;   // 5分钟(秒)
private static final int TOKEN_EXPIRATION = 30;         // 30分钟
2.2 智能刷新逻辑实现
java">// AuthFilter.java 修改后的过滤逻辑
private Mono<Void> handleTokenRefresh(ServerWebExchange exchange, GatewayFilterChain chain,Claims claims,String tokenKey,String originalToken) {// 计算剩余时间long remainingSec = (claims.getExpiration().getTime() - System.currentTimeMillis()) / 1000;// 阶段判断if (remainingSec > WARNING_THRESHOLD) {return Mono.empty();}// 获取分布式锁String lockKey = "token_lock:" + tokenKey;return redisService.lock(lockKey, 10, TimeUnit.SECONDS).flatMap(lockAcquired -> {if (!lockAcquired) return Mono.empty();try {// 双重检查Claims latestClaims = JwtUtils.parseToken(originalToken);long newRemaining = (latestClaims.getExpiration().getTime() - System.currentTimeMillis()) / 1000;if (newRemaining > WARNING_THRESHOLD) {return Mono.empty();}// 处理不同区间if (newRemaining > CRITICAL_THRESHOLD) {// 仅续期RedisredisService.expire(tokenKey, TOKEN_EXPIRATION, TimeUnit.MINUTES);log.info("Redis TTL extended for {}", tokenKey);} else {// 生成新令牌String newToken = JwtUtils.refreshToken(latestClaims, TOKEN_EXPIRATION);redisService.setEx(tokenKey, newToken, TOKEN_EXPIRATION, TimeUnit.MINUTES);exchange.getResponse().getHeaders().add("X-New-Token", newToken);mutateHeader(exchange.getRequest().mutate(), newToken);}return chain.filter(exchange);} finally {redisService.unlock(lockKey);}});
}private void mutateHeader(ServerHttpRequest.Builder mutate, String newToken) {mutate.headers(headers -> {headers.remove(TokenConstants.AUTHENTICATION);headers.add(TokenConstants.AUTHENTICATION, TokenConstants.PREFIX + newToken);});
}

3. 客户端适配方案

3.1 前端自动令牌管理
javascript">// axios全局配置
const instance = axios.create();instance.interceptors.response.use(response => {const newToken = response.headers['x-new-token'];if (newToken) {// 更新本地存储localStorage.setItem('token', newToken);// 重发原始请求(需特殊头标记)if (!response.config.headers['X-No-Retry']) {const retryConfig = {...response.config,headers: {...response.config.headers,'Authorization': `Bearer ${newToken}`,'X-No-Retry': 'true'}};return instance(retryConfig);}}return response;
}, error => {if (error.response?.status === 401) {// 处理令牌失效}return Promise.reject(error);
});
3.2 心跳检测机制
javascript">// 定时检测令牌状态
setInterval(() => {const token = localStorage.getItem('token');if (!token) return;const remaining = calculateTokenRemaining(token); // 解析JWT过期时间if (remaining > 5*60 && remaining <= 15*60) {// 触发静默续期fetch('/api/keepalive', {method: 'HEAD',headers: { 'Authorization': `Bearer ${token}` }});}
}, 120_000); // 每2分钟检测

4. 服务端配套改造

4.1 新增心跳接口
java">@RestController
public class KeepaliveController {@RequestMapping("/api/keepalive")public Mono<Void> keepAlive() {return Mono.empty(); // 仅触发过滤器逻辑}
}
4.2 Redis操作增强
java">// RedisService.java 新增方法
public Mono<Boolean> lock(String key, long timeout, TimeUnit unit) {return redisTemplate.execute(new RedisCallback<>() {@Overridepublic Boolean doInRedis(RedisConnection connection) {return connection.set(key.getBytes(),"1".getBytes(),Expiration.from(timeout, unit),RedisStringCommands.SetOption.SET_IF_ABSENT);}});
}public Mono<Boolean> unlock(String key) {return redisTemplate.delete(key);
}

5. 安全增强措施

5.1 JWT绑定设备指纹
java">// 生成令牌时加入指纹
public static String createToken(LoginUser user, String deviceFingerprint) {return Jwts.builder()// ...其他声明....claim("fingerprint", Hashing.sha256().hashString(deviceFingerprint)).compact();
}// 验证时检查指纹
private boolean validateFingerprint(Claims claims, HttpServletRequest request) {String clientFingerprint = buildFingerprint(request); // 根据IP+UA生成String storedFingerprint = claims.get("fingerprint", String.class);return storedFingerprint.equals(Hashing.sha256().hashString(clientFingerprint));
}
5.2 限流防护配置
# 网关限流配置
spring:cloud:gateway:routes:- id: auth_routeuri: lb://user-servicepredicates:- Path=/api/**filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 10   # 每秒10个redis-rate-limiter.burstCapacity: 20   # 峰值20key-resolver: "#{@userKeyResolver}"

6. 验证与监控

6.1 测试用例
java">@Test
void testMultiStageRefresh() {// 生成初始令牌String token = JwtUtils.createToken("user1", "1001", "Alice", 30);// 模拟20分钟后请求(剩余10分钟)Claims claims = JwtUtils.parseToken(token);claims.setExpiration(new Date(System.currentTimeMillis() - 20*60*1000));// 触发过滤器ServerWebExchange exchange = createExchangeWithToken(token);filter.filter(exchange, chain).block();// 验证Redis续期但未生成新令牌assertNull(exchange.getResponse().getHeaders().get("X-New-Token"));assertTrue(redisService.getExpire(tokenKey) > 25*60);
}
6.2 监控指标
监控项指标类型报警阈值
token_refresh_totalCounterN/A
refresh_conflict_rateGauge>20% (持续5分钟)
redis_lock_wait_timeHistogramP99 > 500ms

7. 部署流程

  1. 顺序部署

    配置中心
    网关服务
    Redis集群
    业务服务
    前端应用
  2. 灰度策略

    • 第一阶段:10%流量开启新逻辑
    • 第二阶段:50%流量+增强监控
    • 全量部署:验证错误率<0.1%
  3. 回滚方案

    • 快速回退开关:
      java">@Value("${token.refresh.enabled:true}")
      private boolean refreshEnabled;if (refreshEnabled) {// 执行新逻辑
      }
      

该方案通过以下创新点实现优化:

  1. 双阈值智能判断:区分续期与刷新场景
  2. 动静结合续期:减少JWT生成次数(降低30% Redis压力)
  3. 分布式锁保障:采用RedLock算法防止集群环境下的并发问题
  4. 客户端无缝衔接:自动重试机制确保请求连续性

实际使用需观察:

  • Redis内存增长趋势
  • 网关P99延迟变化
  • 客户端错误日志中的401异常率

http://www.ppmy.cn/devtools/163440.html

相关文章

20250227解决飞凌OK3588-C的linux R4通过adb拷贝文件速度过慢的问题

20250227解决飞凌OK3588-C的linux R4通过adb拷贝文件速度过慢的问题 2025/2/27 16:51 缘起&#xff1a;最近测试OK3588-C的最新的R1版本的SDK&#xff0c;adb pull的速度为28.8 MB/s Z:\version\OK3588-C_Linux5.10.209Qt5.15.10_用户资料_R1 我司使用4线的USB2.0&#xff0c;…

ERROR:This version of pnpm requires at least Node.js vXXX 的解决方案

This version of pnpm requires at least Node.js vXXX 的解决方案 Centos7环境下&#xff0c;pnpm与Node.js版本不兼容导致报错 This version of pnpm requires at least Node.js vXXX 的解决方案 错误原因其实就是 pnpm 的版本不兼容 Node.js 的版本&#xff0c;明白了可以…

HOW POWERFUL ARE GRAPH NEURAL NETWORKS?(GIN)

GIN——Graph Isomorphism Network normal message deliverer related work GraphSAGE(Inductive Representation Learning on Large Graphs) 突破传统基于矩阵分解的节点嵌入方式&#xff0c;GraphSAGE 通过采样和聚合节点局部邻域的特征信息来生成嵌入&#xff0c;同时学习…

阿里云ECS通用计算

阿里云ECS通用计算概述 阿里云ECS&#xff08;Elastic Compute Service&#xff09;是阿里云提供的一款灵活、高效的云计算服务&#xff0c;它允许用户在云端部署虚拟计算实例&#xff0c;进行各种计算任务。通用计算型实例是阿里云ECS实例的一种类型&#xff0c;专门为中低负…

【备份】php项目处理跨域请求踩坑

这都是老生常谈的东西了。我还在踩坑&#xff0c;记录一下。 我在项目入口明明写了如下代码&#xff1a; // 处理预检请求 (OPTIONS) if ($_SERVER[REQUEST_METHOD] OPTIONS) {header("Access-Control-Allow-Origin: https://xxx.vip");header("Access-Cont…

redis批量删除namespace下的数据

在开发中为了更好的管理数据&#xff0c;对redis进行了分组存储操作&#xff0c;在存值时加了命名空间来实现&#xff0c;如下&#xff1a;Cacheable的value来实现分组 Cacheable(value "config",key "#comparamid_#comCode" )/* */ Query(value "…

校园快递平台系统(小程序论文源码调试讲解)

第4章 系统设计 用户对着浏览器操作&#xff0c;肯定会出现某些不可预料的问题&#xff0c;但是不代表着系统对于用户在浏览器上的操作不进行处理&#xff0c;所以说&#xff0c;要提前考虑可能会出现的问题。 4.1 系统设计思想 系统设计&#xff0c;肯定要把设计的思想进行统…

2024华为OD机试真题-根据某条件聚类最少交换次数(C++/Java/Python)-E卷-100分

2024华为OD机试最新E卷题库-(C卷+D卷+E卷)-(JAVA、Python、C++) 目录 题目描述 输入描述 输出描述 用例1 题目解析 代码 c++ python java 题目描述 给出数字 K,请输出所有结果小于 K 的整数组合到一起的最少交换次数。 组合一起是指满足条件的数字相邻,不要求相邻…