From bb5ceb1dc03637cd07f5d58e7e790aa041c8b880 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 2 Apr 2025 06:46:57 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=20Token-Session?= =?UTF-8?q?=20=E8=8E=B7=E5=8F=96=E7=AE=97=E6=B3=95=EF=BC=8C=E5=87=8F?= =?UTF-8?q?=E5=B0=91=E7=BC=93=E5=AD=98=E8=AF=BB=E5=8F=96=E6=AC=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/dev33/satoken/error/SaErrorCode.java | 3 + .../java/cn/dev33/satoken/stp/StpLogic.java | 95 ++++++++++++------- .../cn/dev33/satoken/strategy/SaStrategy.java | 2 +- sa-token-doc/fun/token-timeout.md | 6 +- 4 files changed, 69 insertions(+), 37 deletions(-) diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java b/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java index fd76ff53..dca784e8 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java @@ -128,6 +128,9 @@ public interface SaErrorCode { /** 获取 Token-Session 时提供的 token 为空 */ int CODE_11073 = 11073; + /** 获取 Token-Session 时提供的 token 为无效 token */ + int CODE_11074 = 11074; + // ------------ diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java index 09a38660..df9275ea 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -1094,25 +1094,11 @@ public class StpLogic { } // 7、token 活跃频率检查 - if(isOpenCheckActiveTimeout()) { - // storage.get(key, () -> {}) 可以避免一次请求多次校验,造成不必要的性能消耗 - SaHolder.getStorage().get(SaTokenConsts.TOKEN_ACTIVE_TIMEOUT_CHECKED_KEY, () -> { + checkActiveTimeoutByConfig(tokenValue); - // 7.1、检查此 token 的最后活跃时间是否已经超过了 active-timeout 的限制,如果是则代表其已被冻结,需要抛出:token 已被冻结 - checkActiveTimeout(tokenValue); + // ------ 至此,loginId 已经是一个合法的值,代表当前会话是一个正常的登录状态了 - // ------ 至此,loginId 已经是一个合法的值,代表当前会话是一个正常的登录状态了 - - // 7.2、如果配置了自动续签功能, 则: 更新这个 token 的最后活跃时间 (注意此处的续签是在续 active-timeout,而非 timeout) - if(SaStrategy.instance.autoRenew.apply(this)) { - updateLastActiveToNow(tokenValue); - } - - return true; - }); - } - - // 9、返回 loginId + // 8、返回 loginId return loginId; } @@ -1277,16 +1263,26 @@ public class StpLogic { // ---- 其它操作 /** - * 判断一个 loginId 是否是有效的 + * 判断一个 loginId 是否是有效的 (判断标准:不为 null、空字符串,且不在异常标记项里面) * * @param loginId 账号id * @return / */ public boolean isValidLoginId(Object loginId) { - // 判断标准:不为 null、空字符串,且不在异常标记项里面 return SaFoxUtil.isNotEmpty(loginId) && !NotLoginException.ABNORMAL_LIST.contains(loginId.toString()); } + /** + * 判断一个 token 是否是有效的 (判断标准:使用此 token 查询到的 loginId 不为 Empty ) + * + * @param tokenValue / + * @return / + */ + public boolean isValidToken(String tokenValue) { + Object loginId = getLoginIdByToken(tokenValue); + return SaFoxUtil.isNotEmpty(loginId); + } + /** * 存储 token - id 映射关系 * @@ -1454,10 +1450,32 @@ public class StpLogic { * @return session对象 */ public SaSession getTokenSessionByToken(String tokenValue, boolean isCreate) { + // 1、token 为空,不允许创建 if(SaFoxUtil.isEmpty(tokenValue)) { throw new SaTokenException("Token-Session 获取失败:token 为空").setCode(SaErrorCode.CODE_11073); } - return getSessionBySessionId(splicingKeyTokenSession(tokenValue), isCreate, null, session -> { + + // 2、如果能查询到旧记录,则直接返回 + String sessionId = splicingKeyTokenSession(tokenValue); + SaSession tokenSession = getSaTokenDao().getSession(sessionId); + if(tokenSession != null) { + return tokenSession; + } + // 以下是查不到的情况 + + // 3、指定了不需要创建,返回 null + if( ! isCreate) { + return null; + } + // 以下是需要创建的情况 + + // 4、检查一下这个 token 是否为有效 token,无效 token 不允许创建 + if(getConfigOrGlobal().getTokenSessionCheckLogin() && ! isValidToken(tokenValue)) { + throw new SaTokenException("Token-Session 获取失败,token 无效: " + tokenValue).setCode(SaErrorCode.CODE_11074); + } + + // 5、创建 Token-Session 并返回 + return getSessionBySessionId(sessionId, true, null, session -> { // 这里是该 Token-Session 首次创建时才会被执行的方法: // 设定这个 SaSession 的各种基础信息:类型、账号体系、Token 值 session.setType(SaTokenConsts.SESSION_TYPE__TOKEN); @@ -1483,20 +1501,8 @@ public class StpLogic { * @return Session对象 */ public SaSession getTokenSession(boolean isCreate) { - - // 1、如果配置了:tokenSessionCheckLogin == true,则需要先校验当前是否登录,未登录情况下不允许拿到 Token-Session - if(getConfigOrGlobal().getTokenSessionCheckLogin()) { - checkLogin(); - } - - // 2、如果前端根本没有提供 Token ,则直接返回 null String tokenValue = getTokenValue(); - if(SaFoxUtil.isEmpty(tokenValue)) { - throw new SaTokenException("Token-Session 获取失败:token 为空").setCode(SaErrorCode.CODE_11073); - } - - // 3、代码至此:tokenSessionCheckLogin 校验通过、且 Token 有值 - // 现在根据前端提供的 Token 获取它对应的 Token-Session 对象(SaSession) + checkActiveTimeoutByConfig(tokenValue); return getTokenSessionByToken(tokenValue, isCreate); } @@ -1682,6 +1688,29 @@ public class StpLogic { return false; } + /** + * 根据全局配置决定是否校验指定 token 的活跃度 + * + * @param tokenValue 指定 token + */ + public 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; + }); + } + } + /** * 检查指定 token 是否已被冻结,如果是则抛出异常 * diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java index df436e5c..5e461a73 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java @@ -162,7 +162,7 @@ public final class SaStrategy { }; /** - * 是否自动续期 + * 是否自动续期 active-timeout */ public SaAutoRenewFunction autoRenew = (stpLogic) -> { return stpLogic.getConfigOrGlobal().getAutoRenew(); diff --git a/sa-token-doc/fun/token-timeout.md b/sa-token-doc/fun/token-timeout.md index 978292be..c4d11900 100644 --- a/sa-token-doc/fun/token-timeout.md +++ b/sa-token-doc/fun/token-timeout.md @@ -48,7 +48,7 @@ sa-token.active-timeout=-1 ### 关于active-timeout的续签 -如果`active-timeout`配置了大于零的值,Sa-Token 会在登录时开始计时,在每次直接或间接调用`getLoginId()`时进行一次冻结检查与续签操作。 +如果`active-timeout`配置了大于零的值,Sa-Token 会在登录时开始计时,在每次直接或间接调用`getLoginId()`、`getTokenSession()`时进行一次冻结检查与续签操作。 此时会有两种情况: 1. 一种是会话无操作时间太长,Token已经被冻结,此时框架会抛出`NotLoginException`异常(场景值=-3), 2. 另一种则是会话在`active-timeout`有效期内通过检查,此时Token可以成功续签 @@ -85,8 +85,8 @@ StpUtil.stpLogic.updateLastActiveToNow(tokenValue); 两者的认证逻辑彼此独立,互不干扰,可以同时使用。 -### StpUtil 类中哪些公开方法支持自动续签 active-timeout? -> 直接或间接获取了当前用户id的方法 (间接调用过 StpLogic.getLoginId() 方法) +### StpUtil 类中哪些方法支持自动续签 active-timeout? +> 直接或间接调用过 `getLoginId()`、`getTokenSession()` 的方法 | 包括但不限于这些: | |---|