mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-04-05 17:37:53 +08:00
增加循环生成 token 的算法,用于确保 Token 的唯一性
This commit is contained in:
parent
ca29da17ee
commit
a72ba8379b
sa-token-core/src/main/java/cn/dev33/satoken
config
fun
stp
strategy
util
sa-token-demo/sa-token-demo-springboot-redis/src/main
@ -39,9 +39,13 @@ public class SaTokenConfig implements Serializable {
|
||||
*/
|
||||
private int maxLoginCount = 12;
|
||||
|
||||
/** 在每次创建 token 时的最高循环次数,用于保证 token 唯一性(-1=不循环尝试,直接使用) */
|
||||
private int maxTryTimes = 12;
|
||||
|
||||
/** 是否尝试从请求体里读取token */
|
||||
private Boolean isReadBody = true;
|
||||
|
||||
|
||||
/** 是否尝试从header里读取token */
|
||||
private Boolean isReadHeader = true;
|
||||
|
||||
@ -205,6 +209,22 @@ public class SaTokenConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 在每次创建 token 时的最高循环次数,用于保证 token 唯一性(-1=不循环尝试,直接使用)
|
||||
*/
|
||||
public int getMaxTryTimes() {
|
||||
return maxTryTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxTryTimes 在每次创建 token 时的最高循环次数,用于保证 token 唯一性(-1=不循环尝试,直接使用)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setMaxTryTimes(int maxTryTimes) {
|
||||
this.maxTryTimes = maxTryTimes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从请求体里读取token
|
||||
*/
|
||||
@ -411,7 +431,7 @@ public class SaTokenConfig implements Serializable {
|
||||
* @param logLevelInt 日志等级 int 值(1=trace、2=debug、3=info、4=warn、5=error、6=fatal)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setLogLeveInt(int logLevelInt) {
|
||||
public SaTokenConfig setLogLevelInt(int logLevelInt) {
|
||||
this.logLevelInt = logLevelInt;
|
||||
this.logLevel = SaFoxUtil.translateLogLevelToString(logLevelInt);
|
||||
return this;
|
||||
@ -521,7 +541,8 @@ public class SaTokenConfig implements Serializable {
|
||||
+ ", activityTimeout=" + activityTimeout
|
||||
+ ", isConcurrent=" + isConcurrent
|
||||
+ ", isShare=" + isShare
|
||||
+ ", maxLoginCount=" + maxLoginCount
|
||||
+ ", maxLoginCount=" + maxLoginCount
|
||||
+ ", maxTryTimes=" + maxTryTimes
|
||||
+ ", isReadBody=" + isReadBody
|
||||
+ ", isReadHeader=" + isReadHeader
|
||||
+ ", isReadCookie=" + isReadCookie
|
||||
@ -544,65 +565,4 @@ public class SaTokenConfig implements Serializable {
|
||||
+ "]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 getIsReadHeader() ,使用方式保持不变 </h1>
|
||||
* @return 是否尝试从header里读取token
|
||||
*/
|
||||
@Deprecated
|
||||
public Boolean getIsReadHead() {
|
||||
return isReadHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 setIsReadHeader() ,使用方式保持不变 </h1>
|
||||
* @param isReadHead 是否尝试从header里读取token
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaTokenConfig setIsReadHead(Boolean isReadHead) {
|
||||
this.isReadHeader = isReadHead;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 getSameTokenTimeout() ,使用方式保持不变 </h1>
|
||||
* @return Id-Token的有效期 (单位: 秒)
|
||||
*/
|
||||
@Deprecated
|
||||
public long getIdTokenTimeout() {
|
||||
return sameTokenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 setSameTokenTimeout() ,使用方式保持不变 </h1>
|
||||
* @param idTokenTimeout Id-Token的有效期 (单位: 秒)
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaTokenConfig setIdTokenTimeout(long idTokenTimeout) {
|
||||
this.sameTokenTimeout = idTokenTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 getCheckSameToken() ,使用方式保持不变 </h1>
|
||||
* @return 是否校验Id-Token(部分rpc插件有效)
|
||||
*/
|
||||
@Deprecated
|
||||
public Boolean getCheckIdToken() {
|
||||
return checkSameToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 setCheckSameToken() ,使用方式保持不变 </h1>
|
||||
* @param checkIdToken 是否校验Id-Token(部分rpc插件有效)
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaTokenConfig setCheckIdToken(Boolean checkIdToken) {
|
||||
this.checkSameToken = checkIdToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package cn.dev33.satoken.fun;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 生成唯一式 token 的方法签名
|
||||
*
|
||||
* @author click33
|
||||
* @since 2023/4/30
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaGenerateUniqueTokenFunction {
|
||||
|
||||
/**
|
||||
* 封装 token 生成、校验的代码,生成唯一式 token
|
||||
*
|
||||
* @param elementName 要生成的元素名称,方便抛出异常时组织提示信息
|
||||
* @param maxTryTimes 最大尝试次数
|
||||
* @param createTokenFunction 创建 token 的函数
|
||||
* @param checkTokenFunction 校验 token 是否唯一的函数(返回 true 表示唯一,可用)
|
||||
* @return 最终生成的唯一式 token
|
||||
*/
|
||||
public String execute(
|
||||
String elementName,
|
||||
int maxTryTimes,
|
||||
Supplier<String> createTokenFunction,
|
||||
Function<String, Boolean> checkTokenFunction
|
||||
);
|
||||
|
||||
}
|
@ -421,8 +421,17 @@ public class StpLogic {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果代码走到此处,说明未能成功复用旧Token,需要新建Token
|
||||
return createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout(), loginModel.getExtraData());
|
||||
// 如果代码走到此处,说明未能成功复用旧Token,需要新建Token
|
||||
return SaStrategy.me.generateUniqueToken.execute(
|
||||
"token",
|
||||
getConfigOfMaxTryTimes(),
|
||||
() -> {
|
||||
return createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout(), loginModel.getExtraData());
|
||||
},
|
||||
tokenValue -> {
|
||||
return getLoginIdNotHandle(tokenValue) == null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// --- 注销
|
||||
@ -1036,7 +1045,17 @@ public class StpLogic {
|
||||
*/
|
||||
if(isCreate) {
|
||||
// 随机创建一个 Token
|
||||
tokenValue = createTokenValue(null, null, getConfig().getTimeout(), null);
|
||||
tokenValue = SaStrategy.me.generateUniqueToken.execute(
|
||||
"token",
|
||||
getConfigOfMaxTryTimes(),
|
||||
() -> {
|
||||
return createTokenValue(null, null, getConfig().getTimeout(), null);
|
||||
},
|
||||
token -> {
|
||||
return getTokenSessionByToken(token, false) == null;
|
||||
}
|
||||
);
|
||||
|
||||
// 写入 [最后操作时间]
|
||||
setLastActivityToNow(tokenValue);
|
||||
// 在当前上下文写入此 TokenValue
|
||||
@ -2305,6 +2324,14 @@ public class StpLogic {
|
||||
return (int) timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回全局配置的 maxTryTimes 值,在每次创建 token 时,对其唯一性测试的最高次数(-1=不测试)
|
||||
* @return /
|
||||
*/
|
||||
public int getConfigOfMaxTryTimes() {
|
||||
return getConfig().getMaxTryTimes();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回持久化对象
|
||||
|
@ -1,5 +1,14 @@
|
||||
package cn.dev33.satoken.strategy;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.annotation.*;
|
||||
import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.fun.SaGenerateUniqueTokenFunction;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
@ -8,18 +17,7 @@ import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.annotation.SaCheckBasic;
|
||||
import cn.dev33.satoken.annotation.SaCheckDisable;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Sa-Token 策略对象
|
||||
@ -47,9 +45,7 @@ public final class SaStrategy {
|
||||
*/
|
||||
public static final SaStrategy me = new SaStrategy();
|
||||
|
||||
//
|
||||
// 所有策略
|
||||
//
|
||||
// ----------------------- 所有策略
|
||||
|
||||
/**
|
||||
* 创建 Token 的策略
|
||||
@ -194,11 +190,38 @@ public final class SaStrategy {
|
||||
me.getAnnotation.apply(method.getDeclaringClass(), annotationClass) != null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成唯一式 token 的算法
|
||||
* <p> 参数 [元素名称, 最大尝试次数, 创建 token 函数, 检查 token 函数]
|
||||
*/
|
||||
public SaGenerateUniqueTokenFunction generateUniqueToken = (elementName, maxTryTimes, createTokenFunction, checkTokenFunction) -> {
|
||||
|
||||
//
|
||||
// 重写策略 set连缀风格
|
||||
//
|
||||
|
||||
// 为方便叙述,以下代码注释均假设在处理生成 token 的场景,但实际上本方法也可能被用于生成 code、ticket 等
|
||||
|
||||
// 循环生成
|
||||
for (int i = 1; ; i++) {
|
||||
// 生成 token
|
||||
String token = createTokenFunction.get();
|
||||
|
||||
// 如果 maxTryTimes == -1,表示不做唯一性验证,直接返回
|
||||
if (maxTryTimes == -1) {
|
||||
return token;
|
||||
}
|
||||
|
||||
// 如果 token 在DB库查询不到数据,说明是个可用的全新 token,直接返回
|
||||
if (checkTokenFunction.apply(token)) {
|
||||
return token;
|
||||
}
|
||||
|
||||
// 如果已经循环了 maxTryTimes 次,仍然没有创建出可用的 token,那么抛出异常
|
||||
if (i >= maxTryTimes) {
|
||||
throw new SaTokenException(elementName + " 生成失败,已尝试" + i + "次,生成算法过于简单或资源池已耗尽");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ----------------------- 重写策略 set连缀风格
|
||||
|
||||
/**
|
||||
* 重写创建 Token 的策略
|
||||
@ -276,5 +299,17 @@ public final class SaStrategy {
|
||||
this.isAnnotationPresent = isAnnotationPresent;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成唯一式 token 的算法
|
||||
* <p> 参数 [元素名称, 最大尝试次数, 创建 token 函数, 检查 token 函数]
|
||||
*
|
||||
* @param generateUniqueToken /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setGenerateUniqueToken(SaGenerateUniqueTokenFunction generateUniqueToken) {
|
||||
this.generateUniqueToken = generateUniqueToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import cn.dev33.satoken.error.SaErrorCode;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
@ -8,19 +11,10 @@ import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import cn.dev33.satoken.error.SaErrorCode;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* Sa-Token 内部工具类
|
||||
*
|
||||
|
@ -1,10 +1,9 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token整合SpringBoot 示例,整合redis
|
||||
* @author kong
|
||||
|
@ -17,7 +17,7 @@ public class TestController {
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public SaResult test() {
|
||||
System.out.println("------------进来了");
|
||||
System.out.println("------------进来了");
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ sa-token:
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
|
Loading…
Reference in New Issue
Block a user