refactor: 优化 Token-Session 获取算法,减少缓存读取次数

This commit is contained in:
click33 2025-04-02 06:46:57 +08:00
parent f2e9f7c222
commit bb5ceb1dc0
4 changed files with 69 additions and 37 deletions

View File

@ -128,6 +128,9 @@ public interface SaErrorCode {
/** 获取 Token-Session 时提供的 token 为空 */ /** 获取 Token-Session 时提供的 token 为空 */
int CODE_11073 = 11073; int CODE_11073 = 11073;
/** 获取 Token-Session 时提供的 token 为无效 token */
int CODE_11074 = 11074;
// ------------ // ------------

View File

@ -1094,25 +1094,11 @@ public class StpLogic {
} }
// 7token 活跃频率检查 // 7token 活跃频率检查
if(isOpenCheckActiveTimeout()) { checkActiveTimeoutByConfig(tokenValue);
// storage.get(key, () -> {}) 可以避免一次请求多次校验造成不必要的性能消耗
SaHolder.getStorage().get(SaTokenConsts.TOKEN_ACTIVE_TIMEOUT_CHECKED_KEY, () -> {
// 7.1检查此 token 的最后活跃时间是否已经超过了 active-timeout 的限制如果是则代表其已被冻结需要抛出token 已被冻结 // ------ 至此loginId 已经是一个合法的值代表当前会话是一个正常的登录状态了
checkActiveTimeout(tokenValue);
// ------ 至此loginId 已经是一个合法的值代表当前会话是一个正常的登录状态了 // 8返回 loginId
// 7.2如果配置了自动续签功能, : 更新这个 token 的最后活跃时间 注意此处的续签是在续 active-timeout而非 timeout
if(SaStrategy.instance.autoRenew.apply(this)) {
updateLastActiveToNow(tokenValue);
}
return true;
});
}
// 9返回 loginId
return loginId; return loginId;
} }
@ -1277,16 +1263,26 @@ public class StpLogic {
// ---- 其它操作 // ---- 其它操作
/** /**
* 判断一个 loginId 是否是有效的 * 判断一个 loginId 是否是有效的 (判断标准不为 null空字符串且不在异常标记项里面)
* *
* @param loginId 账号id * @param loginId 账号id
* @return / * @return /
*/ */
public boolean isValidLoginId(Object loginId) { public boolean isValidLoginId(Object loginId) {
// 判断标准不为 null空字符串且不在异常标记项里面
return SaFoxUtil.isNotEmpty(loginId) && !NotLoginException.ABNORMAL_LIST.contains(loginId.toString()); 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 映射关系 * 存储 token - id 映射关系
* *
@ -1454,10 +1450,32 @@ public class StpLogic {
* @return session对象 * @return session对象
*/ */
public SaSession getTokenSessionByToken(String tokenValue, boolean isCreate) { public SaSession getTokenSessionByToken(String tokenValue, boolean isCreate) {
// 1token 为空不允许创建
if(SaFoxUtil.isEmpty(tokenValue)) { if(SaFoxUtil.isEmpty(tokenValue)) {
throw new SaTokenException("Token-Session 获取失败token 为空").setCode(SaErrorCode.CODE_11073); 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 首次创建时才会被执行的方法 // 这里是该 Token-Session 首次创建时才会被执行的方法
// 设定这个 SaSession 的各种基础信息类型账号体系Token // 设定这个 SaSession 的各种基础信息类型账号体系Token
session.setType(SaTokenConsts.SESSION_TYPE__TOKEN); session.setType(SaTokenConsts.SESSION_TYPE__TOKEN);
@ -1483,20 +1501,8 @@ public class StpLogic {
* @return Session对象 * @return Session对象
*/ */
public SaSession getTokenSession(boolean isCreate) { public SaSession getTokenSession(boolean isCreate) {
// 1如果配置了tokenSessionCheckLogin == true则需要先校验当前是否登录未登录情况下不允许拿到 Token-Session
if(getConfigOrGlobal().getTokenSessionCheckLogin()) {
checkLogin();
}
// 2如果前端根本没有提供 Token 则直接返回 null
String tokenValue = getTokenValue(); String tokenValue = getTokenValue();
if(SaFoxUtil.isEmpty(tokenValue)) { checkActiveTimeoutByConfig(tokenValue);
throw new SaTokenException("Token-Session 获取失败token 为空").setCode(SaErrorCode.CODE_11073);
}
// 3代码至此tokenSessionCheckLogin 校验通过 Token 有值
// 现在根据前端提供的 Token 获取它对应的 Token-Session 对象SaSession
return getTokenSessionByToken(tokenValue, isCreate); return getTokenSessionByToken(tokenValue, isCreate);
} }
@ -1682,6 +1688,29 @@ public class StpLogic {
return false; 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 是否已被冻结如果是则抛出异常 * 检查指定 token 是否已被冻结如果是则抛出异常
* *

View File

@ -162,7 +162,7 @@ public final class SaStrategy {
}; };
/** /**
* 是否自动续期 * 是否自动续期 active-timeout
*/ */
public SaAutoRenewFunction autoRenew = (stpLogic) -> { public SaAutoRenewFunction autoRenew = (stpLogic) -> {
return stpLogic.getConfigOrGlobal().getAutoRenew(); return stpLogic.getConfigOrGlobal().getAutoRenew();

View File

@ -48,7 +48,7 @@ sa-token.active-timeout=-1
### 关于active-timeout的续签 ### 关于active-timeout的续签
如果`active-timeout`配置了大于零的值Sa-Token 会在登录时开始计时,在每次直接或间接调用`getLoginId()`时进行一次冻结检查与续签操作。 如果`active-timeout`配置了大于零的值Sa-Token 会在登录时开始计时,在每次直接或间接调用`getLoginId()`、`getTokenSession()`时进行一次冻结检查与续签操作。
此时会有两种情况: 此时会有两种情况:
1. 一种是会话无操作时间太长Token已经被冻结此时框架会抛出`NotLoginException`异常(场景值=-3) 1. 一种是会话无操作时间太长Token已经被冻结此时框架会抛出`NotLoginException`异常(场景值=-3)
2. 另一种则是会话在`active-timeout`有效期内通过检查此时Token可以成功续签 2. 另一种则是会话在`active-timeout`有效期内通过检查此时Token可以成功续签
@ -85,8 +85,8 @@ StpUtil.stpLogic.updateLastActiveToNow(tokenValue);
两者的认证逻辑彼此独立,互不干扰,可以同时使用。 两者的认证逻辑彼此独立,互不干扰,可以同时使用。
### StpUtil 类中哪些公开方法支持自动续签 active-timeout? ### StpUtil 类中哪些方法支持自动续签 active-timeout?
> 直接或间接获取了当前用户id的方法 (间接调用过 StpLogic.getLoginId() 方法) > 直接或间接调用过 `getLoginId()`、`getTokenSession()` 的方法
| 包括但不限于这些: | | 包括但不限于这些: |
|---| |---|