简介:这份源码资源面向使用若依框架进行企业级开发的工程师,聚焦于token过期时间的配置调整问题。若依作为轻量级快速开发平台,默认token有效期为30分钟,在部分业务场景下需要延长会话时长,资源给出了前后端一致修改的完整参考。压缩包共6个文件,约7KB,包含后端application.yml、前端request.js、index.html、README.md及.gitignore、.inscode等配置与说明文件,分别对应后端过期参数、前端axios超时设置及项目基础配置。已有196人学习下载。读者可据此掌握将后端expireTime调整为120分钟、前端timeout同步设为7200000毫秒的具体写法,理解前后端过期时间保持一致对避免访问中断的意义,并延伸了解refresh token刷新机制与密码策略、多因素认证等安全措施的配合思路,适合需要快速落地会话控制调整的开发者参考。
1. 若依框架 token 过期设置:从 30 分钟默认值到自定义续期策略
很多用若依(RuoYi)做后台系统的开发者,第一次被业务方追问「为什么用户挂着页面半小时就被踢出去」时,才意识到 token 过期这件事不是改个数字那么简单。若依默认把 token 有效期设成 30 分钟,且采用「固定过期」策略——时间一到,无论用户是否还在操作,一律失效。这在演示环境没问题,一旦上线到需要长时间填表单、传大文件、做审批流的场景,就会变成高频投诉点。这篇笔记围绕若依框架 token 过期设置展开,把源码里几个关键位置、配置项含义、续期逻辑改造、以及我踩过的坑一次讲清楚。适合正在用若依做二次开发、需要自定义登录态时长的后端同学,也适合想搞懂 JWT + Redis 双存储这套机制的人。
2. 若依 token 机制拆解:为什么过期时间藏在三个地方
2.1 从登录接口追到 TokenService
若依的登录态不是纯 JWT 自包含,而是「JWT 携带一个 uuid + Redis 存完整用户信息」的混合模式。这个设计决定了过期时间不止一处配置。先看登录入口,SysLoginService.login校验完账号密码后调用tokenService.createToken(loginUser)。真正决定过期的是TokenService里的两个字段:expireTime(Redis 键的过期秒数)和 JWT 自身的EXPIRE_TIME(分钟)。很多人只改了application.yml里的token.expireTime,结果发现前端还是半小时掉线,就是因为 JWT 那层没同步。
// TokenService.java 关键片段(若依 4.x 常见结构) public class TokenService { // 刷新阈值:剩余有效期低于 20 分钟时才续期,避免每次请求都写 Redis private static final long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; // JWT 自身过期时间,单位分钟,默认 30 private static final long EXPIRE_TIME = 30; public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); // 写入 Redis,并设置 expireTime Map<String, Object> claims = new HashMap<>(); claims.put(Constants.LOGIN_USER_KEY, token); return createToken(claims); } private String createToken(Map<String, Object> claims) { return Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret) .setExpiration(new Date(System.currentTimeMillis() + EXPIRE_TIME * 60 * 1000)) .compact(); } }逻辑说明:createToken先生成一个 uuid 作为 Redis 的 key 后缀,把用户对象序列化存进 Redis 并设置过期时间;再用同一个 uuid 作为 JWT 的 claim 签发 token。请求进来时,过滤器解析 JWT 拿到 uuid,去 Redis 查用户信息。所以真正的「登录态存活」由 Redis 决定,JWT 的expiration只是第一道闸。参数上,EXPIRE_TIME是硬编码常量,expireTime来自配置文件,两者必须一起改,否则会出现「Redis 还有数据但 JWT 已过期」的 401。
2.2 application.yml 里的 token 配置项逐个说
若依把可配置项集中在application.yml的token节点下,常见字段如下表。注意不同小版本字段名略有差异,以你本地源码为准。
| 配置项 | 含义 | 默认值 | 调整建议 |
|---|---|---|---|
| token.header | 前端携带 token 的请求头名 | Authorization | 一般不动 |
| token.secret | JWT 签名密钥 | 固定字符串 | 上线必须换,长度够 |
| token.expireTime | Redis 中登录态过期秒数 | 30 | 按业务设 1800~7200 |
| token.autoRefresh | 是否开启自动续期 | 部分版本无 | 有则设 true |
expireTime单位是分钟,不是秒,这点坑过不少人——写成 3600 以为是 1 小时,实际是 60 小时。改完配置后,TokenService里通过@Value("${token.expireTime}")注入,再乘以 60 转成秒传给 Redis。如果你发现改了没生效,先确认注入的字段有没有被static修饰,静态字段无法被@Value注入,这是很隐蔽的一个翻车点。
2.3 过滤器如何校验过期并触发续期
请求进入后,JwtAuthenticationTokenFilter会拦截并调用tokenService.getLoginUser(request)。这个方法内部先解析 JWT,再从 Redis 取LoginUser,取到后调用verifyToken(loginUser)判断是否需要续期。
public void verifyToken(LoginUser loginUser) { long expireTime = loginUser.getExpireTime(); long currentTime = System.currentTimeMillis(); // 剩余时间不足 20 分钟,自动刷新 if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { refreshToken(loginUser); } }逻辑说明:expireTime是上次写入 Redis 时算出的绝对过期时间戳。每次请求如果发现剩余不足 20 分钟,就重新refreshToken,把 Redis 过期时间往后推,同时更新loginUser里的expireTime。这就是「滑动过期」的雏形。参数MILLIS_MINUTE_TEN控制续期触发点,设太小会导致频繁写 Redis,设太大则续期不及时。我一般把它设成总有效期的三分之一左右,比如总时长 2 小时就设 40 分钟。
3. 改 token 过期时间:三种改法与对应源码位置
3.1 只改配置文件的快速改法
最省事的做法是只动application.yml,适合过期时间需求固定、不要求滑动续期的场景。
# application.yml token: header: Authorization secret: your-own-secret-key-please-change expireTime: 120 # 单位分钟,这里表示 2 小时改完后重启服务。但要注意,JWT 那层的EXPIRE_TIME常量如果还是 30,就会出现「Redis 里登录态 2 小时,但 JWT 30 分钟就过期」的矛盾。所以快速改法必须同步把TokenService.EXPIRE_TIME改成 120。两处一致,才不会出现中途 401。这个改法简单,代价是用户 2 小时一到还是会被强制退出,没有续期。
3.2 开启滑动续期让活跃用户不掉线
要让「用户一直在操作就不掉线」,得依赖verifyToken的续期逻辑。确认你的版本里过滤器调用了它。如果没有,需要手动在getLoginUser里补上。
public LoginUser getLoginUser(HttpServletRequest request) { String token = getToken(request); if (StringUtils.isNotEmpty(token)) { Claims claims = parseToken(token); String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser loginUser = redisCache.getCacheObject(userKey); if (loginUser != null) { verifyToken(loginUser); // 关键:触发续期判断 } return loginUser; } return null; }逻辑说明:每次请求都走一遍verifyToken,剩余时间不足阈值就刷新 Redis 过期时间。这样活跃用户的登录态会一直延续,只有连续不操作超过总时长才会失效。参数上,续期阈值和总时长要配合:总时长 2 小时、阈值 40 分钟,意味着用户只要 40 分钟内有一次请求,就不会掉线。如果业务要求「绝对不超过 8 小时」,还得额外加一个首次登录时间戳做硬上限,否则滑动续期会让登录态无限延长。
3.3 自定义过期策略:按角色区分时长
后台系统常有「管理员短、普通用户长」或「移动端长、Web 端短」的需求。这时固定配置不够用,需要在createToken里按用户属性动态设置。
public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); // 按角色动态计算过期分钟数 long minutes = 30; if (loginUser.getUser().isAdmin()) { minutes = 15; // 管理员敏感,短一点 } else if (isMobile(loginUser)) { minutes = 7 * 24 * 60; // 移动端 7 天 } loginUser.setExpireTime(System.currentTimeMillis() + minutes * 60 * 1000); redisCache.setCacheObject(getTokenKey(token), loginUser, (int) minutes, TimeUnit.MINUTES); return createToken(Collections.singletonMap(Constants.LOGIN_USER_KEY, token), minutes); }逻辑说明:把过期分钟数抽成变量,按角色或终端类型分支计算,再分别写入LoginUser.expireTime、Redis 过期时间和 JWT 的setExpiration。三处必须用同一个minutes,否则又会出现不一致。参数上,移动端设长是因为 App 频繁切后台,短时长体验差;管理员设短是安全考虑。这里isMobile可以读请求头 User-Agent,也可以让前端登录时多传一个终端标识字段。
4. 避坑与排查:token 过期设置里最容易翻车的 5 个点
4.1 改了 expireTime 却不生效
现象:配置文件改成 120,重启后用户还是 30 分钟掉线。原因通常是TokenService里注入expireTime的字段被static修饰,Spring 无法给静态字段注入值,实际用的还是默认或旧值。解决:去掉static,或者改用@PostConstruct手动赋值。排查时在createToken里打一行日志输出实际分钟数,一眼就能看出用的是哪个值。
4.2 Redis 有数据但接口返回 401
现象:Redis 里明明能查到登录态,请求却提示认证失败。原因是 JWT 自身的EXPIRE_TIME比 Redis 短,JWT 先过期,parseToken直接抛异常,根本走不到查 Redis 那步。解决:保证 JWT 过期时间大于等于 Redis 过期时间。我一般让 JWT 比 Redis 多留 5 分钟缓冲,避免边界时刻的玄学问题。
4.3 续期导致登录态无限延长
现象:开了滑动续期后,用户只要不关页面就永远不掉线,安全审计过不了。原因是verifyToken只往后推时间,没有绝对上限。解决:在LoginUser里加一个loginTime首次登录时间戳,续期前判断currentTime - loginTime是否超过硬上限(比如 12 小时),超过就拒绝续期,强制重新登录。
4.4 多节点部署下续期不同步
现象:集群部署时,用户请求打到 A 节点续了期,打到 B 节点却提示过期。原因是各节点本地缓存或时间不同步。若依默认用 Redis 存登录态,本身是共享的,但如果你的版本里加了本地缓存层,就会出现不一致。解决:续期只写 Redis,不写本地缓存;同时确保各节点服务器时间用 NTP 对齐,时间偏差超过续期阈值就会出问题。
4.5 前端没处理 token 过期后的跳转
现象:后端返回 401,前端页面卡死或白屏,用户不知道发生了什么。原因是前端拦截器只处理了业务错误码,没处理 401。解决:在前端请求拦截器里判断响应码,401 时清除本地 token 并跳登录页。若依前端一般在request.js里已有处理,但如果你自定义了过期返回码,记得同步改前端判断逻辑,否则后端改得再对,用户体验还是断的。
5. 进阶技巧:用配置中心动态调整 token 时长
前面几种改法都要重启服务,生产环境频繁重启不现实。更稳的做法是把 token 过期时间抽到配置中心(如 Nacos),配合@RefreshScope实现热更新。核心思路是让TokenService从一个可刷新的配置 Bean 里读时长,而不是读启动时固定的@Value。
@Component @RefreshScope public class TokenProperties { // 从配置中心读取,支持不重启刷新 @Value("${token.expireTime:120}") private long expireTime; @Value("${token.refreshThreshold:40}") private long refreshThreshold; public long getExpireMillis() { return expireTime * 60 * 1000; } public long getThresholdMillis() { return refreshThreshold * 60 * 1000; } }逻辑说明:@RefreshScope让这个 Bean 在配置中心推送新值后被重建,TokenService每次调用getExpireMillis()拿到的都是最新值。这样运营侧发现某类用户掉线频繁,改个配置推送即可,不用发版。参数上,refreshThreshold建议设成expireTime的三分之一,两者都放配置中心,方便联动调整。
验证是否生效,可以写一个简单的检查方法:登录后连续发请求,观察 Redis 里 key 的 TTL 是否在接近阈值时被重置。用redis-cli执行TTL 登录key,如果每次请求后 TTL 都回到接近总时长,说明续期生效;如果 TTL 一直递减到 0,说明续期没触发,回去检查verifyToken有没有被调用。我自己的习惯是,任何涉及登录态的改动,上线前必做三件事:确认三处时间一致、确认续期阈值合理、确认前端 401 处理正常。这三件做完,token 过期相关的投诉基本就清零了。希望帮到你。
本文还有配套的精品资源,点击获取