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

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

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

1. 令牌服务层改造

1.1 JWT工具类增强
// 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 新增阈值常量
// 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 智能刷新逻辑实现
// 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 前端自动令牌管理
// 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 心跳检测机制
// 定时检测令牌状态
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 新增心跳接口
@RestController
public class KeepaliveController {@RequestMapping("/api/keepalive")public Mono<Void> keepAlive() {return Mono.empty(); // 仅触发过滤器逻辑}
}
4.2 Redis操作增强
// 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绑定设备指纹
// 生成令牌时加入指纹
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 测试用例
@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. 回滚方案

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

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

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

实际使用需观察:

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

相关文章:

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

令牌刷新优化方案的详细实现步骤&#xff1a; 1. 令牌服务层改造 1.1 JWT工具类增强 // JwtUtils.java 新增方法 public class JwtUtils {// 生成带动态过期时间的令牌public static String createToken(String subject, String userId, String username, long expirationMi…...

基于 C++ Qt 的 Fluent Design 组件库 QFluentWidgets

简介 QFluentWidgets 是一个基于 Qt 的 Fluent Designer 组件库&#xff0c;内置超过 150 个开箱即用的 Fluent Designer 组件&#xff0c;支持亮暗主题无缝切换和自定义主题色。 编译示例 以 Qt5 为例&#xff08;Qt6 也支持&#xff09;&#xff0c;将 libQFluentWidgets.d…...

ClkLog里程碑:荣获2024上海开源技术应用创新竞赛三等奖

2024年10月&#xff0c;ClkLog团队参加了由上海计算机软件技术开发中心、上海开源信息技术协会联合承办的2024上海数智融合“智慧工匠”选树、“领军先锋”评选活动——开源技术应用创新竞赛。我们不仅成功晋级决赛&#xff0c;还荣获了三等奖&#xff01;这一成就不仅是对ClkL…...

边缘计算收益低的三大指标

边缘计算收益低的三大指标主要包括以下方面&#xff1a; 1. 资源贡献不足&#xff1a; 边缘计算的收益通常基于所提供的带宽、存储和计算资源来计算。如果设备的网络带宽有限、在线时间短或提供的存储容量较小&#xff0c;可能无法满足平台设定的最低贡献标准&#xff0c;从而导…...

C# 确保程序只有一个实例运行

常规需求 C#程序只能运行一次,不能多开: using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Threading; using System.Runtime.InteropServices; using System.Security.Principal; namespace BallLocation {sta…...

有没有什么免费的AI工具可以帮忙做简单的ppt?

互联网各领域资料分享专区(不定期更新): Sheet 正文 1. 博思AIPPT 特点:专为中文用户设计,支持文本/文件导入生成PPT,内置海量模板和智能排版功能,涵盖商务、教育等多种场景。可一键优化布局、配色,并集成AI绘图功能(文生图/图生图)。适用场景:职场汇报、教育培训、商…...

TCP基本入门-简单认识一下什么是TCP

部分内容来源&#xff1a;小林Coding TCP的特点 1.面向连接 一定是“一对一”才能连接&#xff0c;不能像 UDP 协议可以一个主机同时向多个主机发送消息&#xff0c;也就是一对多是无法做到的 2.可靠的 无论的网络链路中出现了怎样的链路变化&#xff0c;TCP 都可以保证一个…...

ollama提问命令行程序demo(python)

import requests import json# 定义请求的 URL 和数据 url http://localhost:11434/api/generate data {"model": "deepseek-r1:1.5b","prompt": "写一首关于春天的诗" }# 发送 POST 请求并以流式方式接收响应 response requests.p…...

校园快递助手小程序毕业系统设计

系统功能介绍 管理员端 1&#xff09;登录&#xff1a;输入账号密码进行登录 2&#xff09;用户管理&#xff1a;查看编辑添加删除 学生信息 3&#xff09;寄件包裹管理&#xff1a;查看所有的包裹信息&#xff0c;及物流信息 4&#xff09;待取件信息&#xff1a;查看已到达的…...

基于MATLAB红外弱小目标检测MPCM算法复现

摘要&#xff1a;本文详细介绍了一种基于人类视觉系统特性的红外弱小目标检测算法——Multiscale patch-based contrast measure (MPCM)。该算法通过增强目标与背景的对比度&#xff0c;有效检测红外图像中的弱小目标&#xff0c;并在MATLAB环境中进行了复现与实验验证。 关键…...

MySQL 的存储引擎有哪些?它们之间有什么区别?

MySQL 支持多种存储引擎&#xff0c;每种存储引擎都有其独特的特性和适用场景。以下是 MySQL 中常见的存储引擎及其主要区别&#xff1a; 1.常见存储引擎及其特点 (1)InnoDB • 事务支持&#xff1a;支持完整的 ACID 特性&#xff0c;适用于需要事务处理的场景。 • 锁机制&…...

windows下适用msvc编译ffmpeg 适用于ffmpeg-7.1

需要的工具: visual studio 2019 (可以是其他版本&#xff0c;只是本人电脑上装的为2019) msys2 ffmpeg-7.1源码 1. 修改msys2_shell.cmd 在msys2目录修改msys2_shell.cmd 打开后找到行set MSYS2_PATH_TYPEinherit 删除开头的rem 2. 运行msys2 运行x64 Native Tools Command …...

Docker快速使用指南

docker pull ubuntu:22.04 //先拉取一个基础镜像&#xff0c;一般是操作系统创建一个Dockerfile&#xff0c;放在任意目录下&#xff0c;内容如下 # 使用 Ubuntu 22.04 作为基础镜像 FROM ubuntu:22.04# 设置环境变量&#xff0c;避免安装过程中出现交互提示 ENV DEBIAN_FRONT…...

Mysql表字段字符集未设置导致乱码问题

项目场景&#xff1a; 在使用mysql的text类型作为字段类型【未设置编码】&#xff0c;且表结构【设置了编码集】的条件下&#xff0c;查询表这个字段会出现乱码的情况。 问题描述 今日测试小伙伴给题主提出了一个bug&#xff0c;数据库当中的text文本字段在存储json的情况下&…...

Git:多人协作

目录 多人协作一 准备工作 开发者1准备工作 开发者2准备工作 协作开发 将内容合并进master 多人协作二 开发者1进行工作 开发者2进行工作 特殊场景 将内容合并进master 之前所学习的Git操作&#xff0c;是为了多人协作开发做铺垫的&#xff0c;因为在公司中&#xf…...

JSX基础 —— 识别JS表达式

在JSX中可以通过 大括号语法 { } 识别JS中的表达式&#xff0c;比如常见的变量、函数调用、方法调用等等 1、使用引号传递字符串 2、使用JavaScript变量 3、函数调用和方法调用 (函数和方法本质没有区别&#xff0c;这里默认&#xff1a; 函数是自己定义的&#xff0c;方法是…...

docker镜像和容器(二)

在开始这篇文章之前&#xff0c;有几个需要了解的概念 docker镜像是什么 docker镜像是什么(有兴趣可以参考一下这篇知乎的回答) 文章这里引用一个回答 电脑装系统的时候&#xff0c;需要一张盘&#xff0c;我们称其为镜像&#xff0c;镜像是一个固定的文件&#xff0c;这次读…...

软件工程复试专业课-测试

测试 1 软件质量2 黑盒测试2.1 概念2.2 等价划分类 2.3 边值分析2.4 错误推测2.5 因果图 3 白盒测试3.1概念3.2 覆盖标准3.2.1 语句覆盖3.2.2 判断覆盖3.2.3 条件覆盖3.2.4 判定/条件覆盖3.2.5 条件组合覆盖3.2.6 路径覆盖 4 软件测试的四个阶段5 测试工具 1 软件质量 定义&…...

Unity XR-XR Interaction Toolkit开发使用方法(十)组件介绍(XR Interaction Group)

目录 一、插件介绍 二、主要组件 XR Interaction Manager XR Controller XR Interactor XR Direct Interactor XR Ray Interactor XR Socket Interactor XR Gaze Interactor 三、XR Interaction Group 1、组件介绍 2、核心功能与特点 优先级与冲突管理 动态交互切…...

【2025.2.25更新】wordpress免费AI插件,文章内容、图片自动生成、视频自动生成、网站AI客服、批量采集文章,内置deepseek联网满血版

wordpress免费AI插件&#xff0c;文章内容、文章图片、长尾关键词、视频自动生成、网站AI客服、批量采集文章&#xff0c;插件已接入腾讯云大模型知识引擎xDeepSeek&#xff0c;基于腾讯云大模型知识引擎xDeepSeek可联网满血版&#xff0c;插件可实现文章生成、长尾关键词生成、…...

ISIS(中间系统到中间系统)——基础

ISIS是一项通用的动态路由协议&#xff0c;其隶属于链路状态路由协议&#xff0c;最初运行与OSI七层的网络层&#xff0c;采用组播地址224.0.0.14和224.0.0.15两个组波段&#xff0c;由于其较高的拓展性与高速收敛&#xff0c;被大多数运营商网络所使用 起源 ISIS最初是由国际…...

DeepSeek 开源狂欢周(二)DeepEP深度技术解析 | 解锁 MoE 模型并行加速

在大模型时代&#xff0c;Mixture-of-Experts (MoE) 模型凭借其强大的容量和高效的计算能力&#xff0c;成为研究和应用的热点。然而&#xff0c;MoE 模型的训练和推理面临着巨大的专家并行通信挑战。近日&#xff0c;DeepSeek 开源了 DeepEP 项目&#xff0c;为解决这一难题提…...

Linux网络之传输层协议(UDP,TCP协议)

目录 重新认识端口号 端口号划分 netstat pidof UDP协议 UDP的特点 面向数据报 UDP的缓冲区 全双工和半双工 TCP协议 TCP的特点 TCP报头分析 源端口&#xff0c;目标端口&#xff0c;数据偏移(报文首部长度) 序号 确认号 窗口 6个标志位 ACK SYN …...

HTML第二节

一.列表 1.列表的简介 2.无序列表 注&#xff1a;1.ul里面只能放li&#xff0c;不能放标题和段落标签 2.li里面可以放标题和段落等内容 3.有序列表 4.定义列表 注&#xff1a;要实现上图的效果需要CSS 二.表格 1.表格介绍 注&#xff1a;1.th有额外的效果&#xff0c;可以…...

坐标变换及视图变换和透视变换(相机透视模型)

文章目录 2D transformationScaleReflectionShear&#xff08;切变&#xff09;Rotation around originTranslationReverse变换顺序复杂变换的分解 齐次坐标&#xff08;Homogenous Coordinates&#xff09;3D transformationScale&TranslationRotation Viewing / Camera t…...

Vue 表单优化:下拉框值改变前的确认提示与还原逻辑实现

在开发表单类功能时&#xff0c;我们经常需要对用户的重要操作进行确认提示&#xff0c;以避免误操作导致的数据丢失或错误。本文将通过一个实际案例&#xff0c;介绍如何在 Vue 中实现下拉框值改变前的确认提示&#xff0c;并在用户取消操作时还原原始值。 场景描述 在项目中…...

使用mermaid查看cursor程序生成的流程图

一、得到cursor生成的流程图文本 cursor写的程序正常运行后&#xff0c;在对话框输入框中输入诸如“请生成扫雷的代码流程图”&#xff0c;然后cursor就把流程图给生成了&#xff0c;但是看到的还是文本的样子&#xff0c;保留这部分内容待用 二、注册一个Mermaid绘图账号 …...

Flask 应用结构与模块化管理详细笔记

1. 代码结构优化&#xff1a;StructureA 最初的 Flask 项目结构适用于小型应用&#xff0c;但不适用于大型应用。为了改进代码结构&#xff0c;我们将 URL 管理应用拆分为多个模块。 1.1 StructureA 目录结构 StructureA |-- .flaskenv |-- app.py |-- views.py |-- templat…...

(八)趣学设计模式 之 装饰器模式!

目录 一、 啥是装饰器模式&#xff1f;二、 为什么要用装饰器模式&#xff1f;三、 装饰器模式的实现方式四、 装饰器模式的优缺点五、 装饰器模式的应用场景六、 装饰器模式 vs 代理模式七、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢…...

JVM线程分析详解

java线程状态&#xff1a; 初始(NEW)&#xff1a;新创建了一个线程对象&#xff0c;但还没有调用start()方法。运行(RUNNABLE)&#xff1a;Java线程中将就绪&#xff08;ready&#xff09;和运行中&#xff08;running&#xff09;两种状态笼统的称为“运行”。 线程对象创建…...