
Sa-Token Token 有效期详解timeout 与 active-timeout 双过期策略及自动续签机制【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token本文基于 Sa-Token 官方文档 Token有效期详解 展开系统讲解 Sa-Token 提供的两种 Token 自动过期策略——timeout长久有效期与active-timeout最低活跃频率的配置方式、冻结与过期语义差异、手动续签 API 以及自动续签的底层实现链路。读完后你可以准确配置 Token 有效期、理解冻结但删除的两种状态区别并能在业务中自定义续签时机。两种过期策略总览Sa-Token 提供两种 Token 自动过期策略分别是timeout与active-timeout配置方法如下sa-token: # token 有效期单位秒默认30天-1代表永不过期 timeout: 2592000 # token 最低活跃频率单位秒如果 token 超过此时间没有访问系统就会被冻结默认-1 代表不限制永不冻结 active-timeout: -1# token 有效期单位秒默认30天-1代表永不过期 sa-token.timeout2592000 # token 最低活跃频率单位秒如果 token 超过此时间没有访问系统就会被冻结默认-1 代表不限制永不冻结 sa-token.active-timeout-1两者的区别可以通过下面的例子体现假设你到银行要存钱首先就要办理一张卡要访问系统接口先登录。银行为你颁发一张储蓄卡系统为你颁发一个 Token以后每次存取钱都要带上这张卡后续每次访问系统都要提交 Token。银行为这张卡设定两个过期时间第一个是timeout代表这张卡的长久有效期就是指这张卡最长能用多久。假设timeout3年那么 3 年后此卡将被银行删除想要继续来银行办理业务必须重新办卡Token 过期后想要访问系统必须重新登录。第二个就是active-timeout代表这张卡的最低活跃频率限制就是指这张卡必须每隔多久来银行一次。假设active-timeout1月如果你超过 1 月不来办一次业务银行就将你的卡冻结列为长期不动户Token 长期不访问系统被冻结但不会被删除。两个过期策略可以单独配置也可以同时配置只要有其中一个有效期超出了范围这张卡就会变得不可用两个有效期只要有一个过期了Token 就无法成功访问系统了。从源码结构看两个配置项默认值定义在 SaTokenConfig 中/** token 有效期单位秒 默认30天-1 代表永久有效 */ private long timeout 60 * 60 * 24 * 30;timeout默认为 30 天2592000 秒与文档描述一致active-timeout默认 -1 代表永不冻结。此外 SaTokenConfig 中保留了Deprecated的activity-timeout旧配置项若误用会输出配置项已过期请更换sa-token.activity-timeout - sa-token.active-timeout的告警升级时需注意区分拼写。timeout长久有效期timeout代表 Token 的长久有效期单位/秒例如将其配置为 259200030 天代表在 30 天后Token 必定过期无法继续使用。timeout无法续签想要继续使用必须重新登录。v1.29.0 版本新增续期方法StpUtil.renewTimeout(100)。timeout的值配置为 -1 后代表永久有效不会过期。源码印证renewTimeout 到底续了什么续期能力的实现位于 StpLogic#renewTimeout其处理流程可以拆分为七步校验 token 指向的 loginId 是否存在为空则直接返回校验 token 合法性查不到对应 Access-Session 会话或终端信息时抛出SaTokenException未能查询到对应 Access-Session 会话无法续期续期 token 本身的有效期改 dao 中 key 的 ttl续期该 token 的 Token-Session 有效期续期该 token 指向账号的 Account-Session 有效期session.updateMinTimeout(timeout)若开启了 active-timeout 检查同步更新最后活跃时间记录的有效期发布SaTokenEventCenter.doRenewTimeout续期事件供全局监听器感知。值得注意的细节在 StpLogic#renewTimeout(long) 中续期缓存数据之外如果开启读 CookieisReadCookie()还会同步续期客户端 Cookie 的有效期当timeout -1永久或超过Integer.MAX_VALUE时由于浏览器一般不支持永久 Cookie代码会将其收敛为Integer.MAX_VALUE避免数据溢出。active-timeout最低活跃频率active-timeout代表最低活跃频率单位/秒例如将其配置为 180030 分钟代表用户如果 30 分钟无操作则此 Token 会立即过期被冻结但不会删除掉。如果在 30 分钟内用户有操作则会再次续签 30 分钟用户如果一直操作则会一直续签直到连续 30 分钟无操作Token 才会过期。active-timeout的值配置为 -1 后代表永久有效不会过期此时也无需频繁续签。源码印证冻结是如何判定的冻结的判定核心是 StpLogic#getTokenActiveTimeoutByToken 中的计算公式// 实际时间差 long timeDiff (System.currentTimeMillis() - lastActiveTime) / 1000; // 该 token 允许的时间差 long allowTimeDiff getTokenUseActiveTimeoutOrGlobalConfig(tokenValue); if(allowTimeDiff SaTokenDao.NEVER_EXPIRE) { // 如果允许的时间差为 -1 则代表永不冻结此处需要立即返回 -1 return SaTokenDao.NEVER_EXPIRE; } // 校验这个时间差是否超过了允许的值 // 计算公式为: 允许的最大时间差 - 实际时间差判断是否 0 如果是则代表已经被冻结 返回-2 long activeTimeout allowTimeDiff - timeDiff; if(activeTimeout 0) { return SaTokenDao.NOT_VALUE_EXPIRE; // -2代表已被冻结 } else { return activeTimeout; // 否则返回剩余活跃有效时间 }返回值的语义约定为-1NEVER_EXPIRE代表永不冻结-2NOT_VALUE_EXPIRE代表已被冻结其余为剩余活跃秒数。StpLogic#isFreeze 正是基于这套返回值判断 token 是否处于冻结状态。这与冻结不删除的语义完全对应被冻结的 Token 在缓存中依然存在最后活跃时间记录还在只是检查时会被拒绝而timeout过期后 key 的 ttl 到期由缓存自然删除。当冻结态 Token 发起请求时StpLogic#checkActiveTimeout 会抛出NotLoginExceptionpublic void checkActiveTimeout(String tokenValue) { if (isFreeze(tokenValue)) { throw NotLoginException.newInstance(loginType, TOKEN_FREEZE, TOKEN_FREEZE_MESSAGE, tokenValue) .setCode(SaErrorCode.CODE_11016); } }其中场景值TOKEN_FREEZE与异常码定义在 NotLoginException/** 表示 token 已被冻结 */ public static final String TOKEN_FREEZE -6; public static final String TOKEN_FREEZE_MESSAGE token 已被冻结;也就是说前端捕获到未登录异常时可以通过场景值-6与11016错误码区分出token 已被冻结这一特定情形与 token 无效-2、超时-4、被顶下线-5等异常场景互不混淆。单元测试印证仓库内置的 StpLogicActiveTimeoutTest 覆盖了上述关键行为例如登录 10001 后调用updateLastActiveToNow()getTokenActiveTimeout()返回值应落在175 ~ 180秒区间配置active-timeout180且checkActiveTimeout()不抛异常将active-timeout配置为 -1 时getTokenActiveTimeoutByToken()应返回SaTokenDao.NEVER_EXPIRE永不冻结配置active-timeout10秒并等待超时后对应方法返回SaTokenDao.NOT_VALUE_EXPIRE冻结态。这些用例与本文计算公式 返回值约定的源码分析一一对应。关于 active-timeout 的续签如果active-timeout配置了大于零的值Sa-Token 会在登录时开始计时在每次直接或间接调用getLoginId()、getTokenSession()时进行一次冻结检查与续签操作。此时会有两种情况一种是会话无操作时间太长Token 已经被冻结此时框架会抛出NotLoginException异常场景值 -6异常信息token 已被冻结另一种则是会话在active-timeout有效期内通过检查此时 Token 可以成功续签。自动续签的实现入口是 StpLogic#checkActiveTimeoutByConfigpublic void checkActiveTimeoutByConfig(String tokenValue) { if(isOpenCheckActiveTimeout()) { // storage.get(key, () - {}) 可以避免一次请求多次校验造成不必要的性能消耗 SaHolder.getStorage().get(SaTokenConsts.TOKEN_ACTIVE_TIMEOUT_CHECKED_KEY, () - { // 1、检查此 token 的最后活跃时间是否已经超过了 active-timeout 的限制 // 如果是则代表其已被冻结需要抛出token 已被冻结 checkActiveTimeout(tokenValue); // 2、如果配置了自动续签功能, 则: 更新这个 token 的最后活跃时间 // 注意此处的续签是在续 active-timeout而非 timeout if(SaStrategy.instance.autoRenew.apply(this)) { updateLastActiveToNow(tokenValue); } return true; }); } }这段代码体现了两个工程细节单次请求内去重借助SaHolder.getStorage().get(key, supplier)同一请求中多次调用getLoginId()等触发续签的方法时冻结检查与续签只执行一次避免不必要的缓存读写续签策略可插拔是否续签由 SaStrategy#autoRenew 这个策略函数决定默认为 true可通过配置项autoRenewfalse或自定义策略覆盖。而续签动作本身 StpLogic#updateLastActiveToNow 的实现非常轻量——把最后活跃时间key 的 value 更新为当前时间戳 本次允许的 active-timeout并刷新该 key 的过期时间public void updateLastActiveToNow(String tokenValue) { String key splicingKeyLastActiveTime(tokenValue); String value new SaValue2Box(System.currentTimeMillis(), getTokenUseActiveTimeout(tokenValue)).toString(); getSaTokenDao().update(key, value); }这正是滑动窗口语义的底层实现每次有效访问都从当前时刻重新起算活跃窗口只要用户持续活跃窗口就不断顺延。手动续签 active-timeout可以如果框架的自动续签算法无法满足您的业务需求你可以进行手动续签Sa-Token 提供两个 API 供你操作StpUtil.checkActiveTimeout()检查当前 Token 是否已经被冻结如果是则抛出异常StpUtil.updateLastActiveToNow()续签当前 Token将 [最后操作时间] 更新为当前时间戳注意在手动续签时即使 Token 已经被冻结也可续签成功解冻如果此场景下需要提示续签失败可采用先检查再续签的形式保证 Token 有效性。例如以下代码// 先检查是否已被冻结 StpUtil.checkActiveTimeout(); // 检查通过后继续续签 StpUtil.updateLastActiveToNow();这一注意事项与 StpLogic#updateLastActiveToNow 的 Javadoc 完全一致请注意: 即使 token 已被冻结 也可续签成功如果此场景下需要提示续签失败可在此之前调用 checkActiveTimeout() 强制检查是否冻结即可——因为续签只是覆写最后活跃时间记录并不会校验当前是否冻结。同时你还可以关闭框架的自动续签在配置文件中配置autoRenewfalse此时续签操作完全由开发者控制框架不再自动进行任何续签操作。对应源码中 SaTokenConfig#autoRenew 默认值为trueprivate Boolean autoRenew true; // 是否打开自动续签 activeTimeout // 如果此值为 true, 框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作如果你需要给其它 Token 续签// 为指定 Token 续签 StpUtil.stpLogic.updateLastActiveToNow(tokenValue);timeout 与 active-timeout 可以同时使用吗可以同时使用两者的认证逻辑彼此独立互不干扰可以同时使用。从源码结构看timeout走的是 token key 自身的缓存 ttl 到期机制而active-timeout依赖独立的最后活跃时间key 做滑动检查两者互不感知同时生效时任何一个到期过期或冻结都会导致该 Token 无法通过认证。StpUtil 类中哪些方法支持自动续签 active-timeout直接或间接调用过getLoginId()、getTokenSession()的方法包括但不限于包括但不限于这些StpUtil.checkLogin()StpUtil.getLoginId()StpUtil.getLoginIdAsInt()StpUtil.getLoginIdAsString()StpUtil.getLoginIdAsLong()---StpUtil.getSession()StpUtil.getTokenSession()---StpUtil.getRoleList()StpUtil.hasRole()StpUtil.hasRoleAnd()StpUtil.hasRoleOr()StpUtil.checkRole()StpUtil.checkRoleAnd()StpUtil.checkRoleOr()---StpUtil.getPermissionList()StpUtil.hasPermission()StpUtil.hasPermissionAnd()StpUtil.hasPermissionOr()StpUtil.checkPermission()StpUtil.checkPermissionAnd()StpUtil.checkPermissionOr()---StpUtil.openSafe()StpUtil.isSafe()StpUtil.checkSafe()StpUtil.getSafeTime()StpUtil.closeSafe()以下注解都间接调用过getLoginId()方法支持自动续签的注解SaCheckLoginSaCheckRoleSaCheckPermissionSaCheckSafe实践建议小结结合本文的机制说明给出几条可直接落地的配置建议场景推荐配置说明管理后台/敏感业务timeout: 86400active-timeout: 1800最长 1 天30 分钟无操作即冻结兼顾安全与体验普通 Web 应用timeout: 2592000默认active-timeout: -1默认30 天长久有效不限制活跃移动端长连接timeout: -1active-timeout: 604800永不自然过期但 7 天不活跃即冻结需要业务自定义续签时机autoRenew: false 手动调用updateLastActiveToNow()框架不再自动续签完全由业务代码控制关键语义再强调一次timeout到期是删除必须重新登录active-timeout到期是冻结记录仍在检查即被拒场景值-6renewTimeout()只能续timeoutupdateLastActiveToNow()只能续active-timeout两者不可互相替代。参考资料仓库内路径文档原文sa-token-doc/fun/token-timeout.md配置项定义SaTokenConfig冻结判定与续签核心实现StpLogic冻结异常定义NotLoginException续签策略函数SaStrategy行为验证用例StpLogicActiveTimeoutTest【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考