2026/10/18 18:32:21

若依框架Token过期机制源码解析与二次开发避坑指南

若依框架Token过期机制源码解析与二次开发避坑指南 简介这份源码资源面向使用若依框架进行企业级开发的工程师聚焦于token过期时间的配置调整问题。资源包共6个文件压缩后约7KB包含后端application.yml、前端request.js、index.html、README.md及.gitignore、.inscode等配置与说明文件分别对应后端有效期参数、前端axios超时设置及项目基础说明。已有196人学习下载。通过这份源码读者可以直观看到如何将后端expireTime由默认30分钟改为120分钟并同步把前端timeout设为7200000毫秒保证前后端token过期策略一致避免因配置不同步导致的访问中断。资源还涉及refresh token刷新机制与安全策略的配合思路适合需要理解若依token管理机制、希望快速完成会话控制调整的开发者参考。1. 从一次凌晨告警说起若依框架的 token 过期到底改哪里凌晨两点被运维电话叫醒说后台管理系统的用户集体掉线重新登录后没几分钟又掉。排查一圈发现是网关层把 token 有效期配成了 30 分钟而前端刷新 token 的逻辑压根没接上。这种翻车现场在若依RuoYi二次开发里太常见了——token 过期时间散落在配置文件、Redis 缓存、JWT 工具类三个地方改一处不生效改两处冲突改三处才勉强跑通。这份源码包把若依框架里 token 过期相关的配置项、刷新机制、Redis 存储结构全部拆开适合正在做若依二次开发、被登录态问题卡住的 Java 后端。不管你是刚接手若依项目的新手还是想搞清楚 JWT 与 Redis 双校验逻辑的老手下面这套从配置到源码的拆解路径都能直接抄作业。2. 若依 token 机制拆解JWT 签发与 Redis 续期的双轨逻辑若依的登录态管理不是单纯的 JWT 无状态方案而是 JWT Redis 的双轨制。理解这一点是改对 token 过期时间的前提否则你会在配置文件里改完发现没生效然后陷入「明明改了怎么还过期」的玄学循环。2.1 JWT 负责什么、Redis 负责什么JWT 本身只承载用户身份信息userId、username、部门等签发时设置一个过期时间这个时间决定了 token 字符串本身的「硬过期」。但若依在 JWT 之外加了一层 Redis 缓存key 是login_tokens:{uuid}value 是登录用户对象同时给这个 key 设置 TTL。每次请求进来过滤器先解析 JWT 拿到 uuid再用 uuid 去 Redis 查用户信息。如果 Redis 里查不到即使 JWT 还没到硬过期时间也会判定为登录失效。这就意味着 token 的实际有效期取 JWT 过期时间和 Redis TTL 中较短的那个。很多开发者只改了application.yml里的token.expireTime以为万事大吉结果 Redis 的 TTL 没同步token 提前失效。常见做法是让 Redis TTL 比 JWT 过期时间略长留出刷新窗口。2.2 配置文件里的三个关键参数若依的 token 相关配置集中在application.yml的token节点下核心参数有三个参数名含义默认值影响范围token.header请求头中 token 的字段名Authorization前端传参、后端拦截器token.secretJWT 签名密钥固定字符串签发与校验改了所有 token 失效token.expireTimetoken 有效期分钟30JWT 硬过期 Redis TTL 基准expireTime的单位是分钟若依在TokenService里会把它换算成毫秒传给 JWT 的setExpiration同时用这个值乘以 60 得到 Redis 的 TTL 秒数。所以改这一个值理论上两处都会变。但实际项目中经常有人在TokenService里硬编码了 Redis 的过期时间导致配置文件和实际行为不一致——这就是下一章要重点排查的坑。2.3 刷新机制什么时候续期、续多久若依默认没有自动刷新 token 的逻辑token 过期就是过期用户必须重新登录。但在实际业务里后台管理系统让用户每 30 分钟登录一次显然不合理。常见的改造方案是在过滤器里加一段「自动续期」逻辑当 token 剩余有效期低于某个阈值比如总时长的 1/3时自动刷新 Redis TTL 并重新签发 JWT。这个阈值不能设得太激进。如果每次请求都刷新Redis 写操作会暴增如果设得太保守用户还是会在操作过程中突然掉线。我一般会把阈值设在总有效期的 20% 到 30% 之间比如 30 分钟有效期剩余 8 分钟以内才触发续期。续期时只更新 Redis TTL 和返回新 token不重新查数据库加载用户信息避免不必要的 IO。// TokenService.java 中刷新 token 的核心逻辑 public void refreshToken(LoginUser loginUser) { // 仅在剩余有效期低于阈值时才刷新避免频繁写 Redis long expireTime loginUser.getExpireTime(); long currentTime System.currentTimeMillis(); long remaining expireTime - currentTime; // 阈值设为总有效期的 20%30分钟有效期即剩余6分钟时触发 long threshold loginUser.getTokenExpireTime() * 0.2; if (remaining threshold) { // 刷新 Redis TTL loginUser.setExpireTime(currentTime loginUser.getTokenExpireTime()); redisCache.setCacheObject(getTokenKey(loginUser.getToken()), loginUser, loginUser.getTokenExpireTime(), TimeUnit.MILLISECONDS); // 重新签发 JWT 并写入响应头 String newToken tokenService.createToken(loginUser); ServletUtils.getResponse().setHeader(tokenConfig.getHeader(), newToken); } }这段代码的关键在于threshold的计算。loginUser.getTokenExpireTime()是 token 的总有效期毫秒数乘以 0.2 得到续期触发阈值。remaining是当前剩余时间只有低于阈值才执行刷新。刷新时同时更新 Redis 缓存和 JWT保证两边一致。注意setCacheObject的第三个参数是毫秒别传成秒否则 Redis TTL 会变成几毫秒token 秒失效。3. 源码级改造从配置文件到 TokenService 的完整链路上一章理清了机制这一章直接动手改。若依的 token 过期设置涉及四个文件application.yml、TokenService.java、JwtAuthenticationTokenFilter.java、SecurityConfig.java。改错任何一个轻则 token 不刷新重则整个登录模块瘫痪。3.1 配置文件与常量类的联动修改先改application.ymltoken: header: Authorization secret: abcdefghijklmnopqrstuvwxyz # token 有效期单位分钟从 30 改为 120 expireTime: 120改完配置后检查TokenService.java里读取配置的方式。若依用的是ConfigurationProperties绑定TokenConfig类里expireTime字段是long类型单位分钟。但TokenService在设置 Redis TTL 时如果直接用了tokenConfig.getExpireTime()而没有乘以 60Redis 的 TTL 就会变成 120 秒而不是 120 分钟。这是最常见的翻车点之一。// TokenService.java 中设置 Redis TTL 的正确写法 public void setLoginUser(LoginUser loginUser) { if (StringUtils.isNotNull(loginUser) StringUtils.isNotEmpty(loginUser.getToken())) { // 注意expireTime 单位是分钟Redis TTL 需要毫秒所以乘以 60 再乘以 1000 long expireMillis tokenConfig.getExpireTime() * 60 * 1000; loginUser.setExpireTime(System.currentTimeMillis() expireMillis); redisCache.setCacheObject(getTokenKey(loginUser.getToken()), loginUser, expireMillis, TimeUnit.MILLISECONDS); } }expireMillis的计算是分钟 × 60 × 1000得到毫秒数。loginUser.setExpireTime记录的是绝对过期时间戳用于后续判断是否续期。redisCache.setCacheObject的第四个参数指定时间单位这里用TimeUnit.MILLISECONDS和expireMillis对应。如果单位传错Redis 会按秒或毫秒误判导致 token 提前失效或永不过期。3.2 过滤器中的 token 校验与续期插入点JwtAuthenticationTokenFilter是每次请求都会经过的过滤器token 校验和续期逻辑都插在这里。若依原生的过滤器只做校验不续期。改造时在getLoginUser之后、SecurityContextHolder设置之前插入续期判断。// JwtAuthenticationTokenFilter.java 中插入续期逻辑 Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { LoginUser loginUser tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser) StringUtils.isNull(SecurityUtils.getAuthentication())) { // 先校验 token 是否在有效期内 tokenService.verifyToken(loginUser); // 插入续期逻辑剩余有效期不足时自动刷新 tokenService.refreshTokenIfNeeded(loginUser); UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } chain.doFilter(request, response); }verifyToken负责检查 Redis 中的登录用户是否还存在、JWT 是否已过期。refreshTokenIfNeeded是自定义方法内部逻辑和上一章的refreshToken一致。注意续期操作要在SecurityContextHolder设置之前完成否则新 token 写不进响应头。另外refreshTokenIfNeeded里要判断请求是否来自登录接口或白名单路径避免对静态资源请求也触发续期白白增加 Redis 压力。3.3 Redis 缓存结构与 TTL 的对应关系若依在 Redis 里存了两类和 token 相关的 keyKey 模式存储内容TTL 设置用途login_tokens:{uuid}LoginUser 对象expireTime × 60 秒校验登录态、续期sys_config:token.expireTime配置值永久动态读取配置可选login_tokens的 TTL 是核心。如果项目里用了 Redis 集群或哨兵模式要注意 TTL 的过期事件通知是否开启。若依默认不依赖过期事件但如果你在续期逻辑里监听了expired事件做清理集群环境下事件可能丢失导致续期判断出错。常见做法是不依赖过期事件每次请求主动查 Redis 判断剩余时间。# 查看 Redis 中 token 的剩余 TTL单位秒 redis-cli TTL login_tokens:550e8400-e29b-41d4-a716-446655440000 # 返回 -1 表示 key 存在但未设置过期时间返回 -2 表示 key 已过期如果TTL返回 -1说明setCacheObject时没传过期时间参数token 永不过期存在安全风险。如果返回 -2说明 token 已失效但 JWT 可能还没到期需要检查两边的过期时间是否同步。4. 避坑排查token 过期设置里最容易翻车的五个点这一章全是血泪经验。下面五个问题是我在若依二次开发中反复遇到的每个都按「现象 → 原因 → 解决」拆开照着排查能省下大量调试时间。4.1 改了 expireTime 但 token 还是 30 分钟过期现象application.yml里expireTime改成 120重启服务后登录token 依然在 30 分钟后失效。原因TokenService里可能硬编码了过期时间或者TokenConfig的expireTime字段没有正确绑定配置。若依某些版本里TokenConfig用的是Value(${token.expireTime})注入如果配置文件里 key 写成了token.expire-time或token.expireTime大小写不一致注入会失败字段保持默认值 30。解决在TokenService的setLoginUser方法里打断点看tokenConfig.getExpireTime()返回多少。如果是 30检查配置文件 key 和TokenConfig字段的映射关系。用ConfigurationProperties(prefix token)时字段名expireTime对应配置expireTime大小写敏感。4.2 Redis TTL 变成负数或秒级失效现象登录后几秒钟 token 就失效Redis 里login_tokens的 TTL 显示为负数或个位数。原因setCacheObject的时间单位传错。若依的RedisCache类默认用秒但TokenService里传的是毫秒值导致 Redis 把 7200000 毫秒当成 7200000 秒或者反过来把 7200 秒当成 7200 毫秒7.2 秒。解决统一单位。RedisCache.setCacheObject如果签名是(key, value, timeout, TimeUnit)就显式传TimeUnit.MILLISECONDS。如果签名只有(key, value, timeout)确认 timeout 的单位是秒还是毫秒再决定传expireTime * 60还是expireTime * 60 * 1000。4.3 续期逻辑导致 token 无限延长现象用户登录后只要一直操作token 永远不过期安全审计不通过。原因续期阈值设得太宽或者每次请求都无条件刷新。比如把阈值设成总有效期的 90%用户操作一次就续期一次token 实际有效期变成无限。解决设置绝对过期上限。在LoginUser里加一个maxExpireTime字段记录首次登录时间加上最大允许时长比如 8 小时。续期时判断当前时间是否超过maxExpireTime超过则强制失效要求重新登录。// 在 LoginUser 中增加最大过期时间字段 private Long maxExpireTime; // 续期时增加上限判断 if (System.currentTimeMillis() loginUser.getMaxExpireTime()) { // 超过最大有效期强制失效 redisCache.deleteObject(getTokenKey(loginUser.getToken())); throw new ServiceException(登录已超时请重新登录); }4.4 多端登录时 token 互相踢下线现象同一账号在 PC 和 App 同时登录后登录的把先登录的踢掉或者两边 token 冲突。原因若依默认一个用户只保留一个 token新登录会覆盖 Redis 里的login_tokens。如果业务需要多端共存需要把 key 改成login_tokens:{uuid}:{deviceType}并在校验时根据设备类型查对应的 key。解决改造TokenService的getTokenKey方法加入设备标识。同时前端传 token 时带上设备类型后端根据设备类型路由到不同的 Redis key。注意多端场景下续期要分别处理不能一个端续期把另一个端的 TTL 也改了。4.5 集群环境下 token 校验时好时坏现象单机环境正常部署到多节点集群后用户操作时偶尔提示登录失效刷新后又正常。原因Redis 集群的节点时间不同步或者 JWT 的签名密钥在多个节点上不一致。若依的token.secret如果每个节点单独配置且值不同A 节点签发的 token 到 B 节点校验会失败。解决确保所有节点的token.secret完全一致最好从配置中心统一读取。Redis 集群用同一个逻辑库避免不同节点连到不同的 Redis 实例。如果用了 Redis 主从注意主从同步延迟续期写主库、读从库时可能读到旧 TTL。5. 进阶技巧用动态配置和监控把 token 过期管起来改完源码只是第一步生产环境里 token 过期时间往往需要根据业务场景动态调整。比如后台管理 2 小时开放 API 15 分钟内部服务调用 5 分钟。硬编码在配置文件里改一次重启一次不现实。5.1 把 expireTime 做成动态配置若依本身有sys_config表可以存系统配置。把token.expireTime写进这张表TokenService每次读取时从缓存或数据库取就能实现不重启调整。// 从 sys_config 动态读取 token 过期时间 public long getDynamicExpireTime() { // 先从 Redis 缓存读缓存没有则查数据库 String configValue redisCache.getCacheObject(sys_config:token.expireTime); if (StringUtils.isEmpty(configValue)) { configValue configService.selectConfigByKey(token.expireTime); redisCache.setCacheObject(sys_config:token.expireTime, configValue, 1, TimeUnit.HOURS); } return Long.parseLong(configValue); }这段代码先从 Redis 读配置读不到再查数据库然后回写缓存 1 小时。这样修改sys_config表后最多 1 小时生效不用重启。注意configService.selectConfigByKey是若依自带的方法直接复用即可。5.2 监控 token 剩余有效期分布光有动态配置还不够得知道线上 token 的实际剩余时间分布。我一般会在TokenService里加一个定时任务每小时统计一次 Redis 中login_tokens:*的 TTL 分布按区间打点上报。TTL 区间含义告警策略 5 分钟即将过期不告警正常续期530 分钟正常范围不告警30 分钟2 小时长有效期记录日志观察是否合理 2 小时异常偏长告警检查是否续期逻辑失控-1永不过期立即告警安全风险# 用 scan 遍历所有 token key 并统计 TTL 分布 redis-cli --scan --pattern login_tokens:* | while read key; do ttl$(redis-cli TTL $key) echo $ttl done | sort -n | uniq -c这个脚本会输出每个 TTL 值对应的 key 数量快速看出有没有异常。--scan比keys安全不会阻塞 Redis。统计结果可以接入 Prometheus 或直接写日志配合告警规则使用。5.3 一个容易被忽略的细节时区与时间戳最后说一个我踩过的坑。若依的LoginUser.expireTime存的是System.currentTimeMillis()这是 UTC 时间戳不受时区影响。但如果你在续期逻辑里用了LocalDateTime或Date做比较服务器时区设置不对就会导致续期判断偏差几个小时。从那以后我每次写时间比较都强制用System.currentTimeMillis()绝不用new Date().getTime()之外的任何时间 API。希望帮到你。本文还有配套的精品资源点击获取