mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-04-05 17:37:53 +08:00
Merge branch 'dromara:dev' into dev
This commit is contained in:
commit
70073a0334
@ -1,8 +1,5 @@
|
||||
package cn.dev33.satoken;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.config.SaTokenConfigFactory;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
@ -19,7 +16,6 @@ import cn.dev33.satoken.log.SaLog;
|
||||
import cn.dev33.satoken.log.SaLogForConsole;
|
||||
import cn.dev33.satoken.same.SaSameTemplate;
|
||||
import cn.dev33.satoken.sign.SaSignTemplate;
|
||||
import cn.dev33.satoken.sign.SaSignTemplateDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
@ -28,6 +24,9 @@ import cn.dev33.satoken.temp.SaTempDefaultImpl;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理 Sa-Token 所有全局组件
|
||||
* @author kong
|
||||
@ -209,7 +208,7 @@ public class SaManager {
|
||||
if (saSignTemplate == null) {
|
||||
synchronized (SaManager.class) {
|
||||
if (saSignTemplate == null) {
|
||||
SaManager.saSignTemplate = new SaSignTemplateDefaultImpl();
|
||||
SaManager.saSignTemplate = new SaSignTemplate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,111 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
/**
|
||||
* Sa-Token API 接口签名/验签 相关配置类
|
||||
*
|
||||
* @author click33
|
||||
* @since 2023/5/2
|
||||
*/
|
||||
public class SaSignConfig {
|
||||
|
||||
/**
|
||||
* API 调用签名秘钥
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距,默认15分钟
|
||||
*/
|
||||
private long timestampDisparity = 1000 * 60 * 15;
|
||||
|
||||
/**
|
||||
* 是否校验 nonce 随机字符串
|
||||
*/
|
||||
private Boolean isCheckNonce = true;
|
||||
|
||||
|
||||
/**
|
||||
* 获取 API 调用签名秘钥
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String getSecretKey() {
|
||||
return this.secretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 API 调用签名秘钥
|
||||
*
|
||||
* @param secretKey /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSignConfig setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距,默认15分钟
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public long getTimestampDisparity() {
|
||||
return this.timestampDisparity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距,默认15分钟
|
||||
*
|
||||
* @param timestampDisparity /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSignConfig setTimestampDisparity(long timestampDisparity) {
|
||||
this.timestampDisparity = timestampDisparity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 是否校验 nonce 随机字符串
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public Boolean getIsCheckNonce() {
|
||||
return this.isCheckNonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 是否校验 nonce 随机字符串
|
||||
*
|
||||
* @param isCheckNonce /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSignConfig setIsCheckNonce(Boolean isCheckNonce) {
|
||||
this.isCheckNonce = isCheckNonce;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算保存 nonce 时应该使用的 ttl,单位:秒
|
||||
* @return /
|
||||
*/
|
||||
public long getSaveNonceExpire() {
|
||||
// 如果 timestampDisparity >= 0,则 nonceTtl 的值等于 timestampDisparity 的值,单位转秒
|
||||
if(timestampDisparity >= 0) {
|
||||
return timestampDisparity / 1000;
|
||||
}
|
||||
// 否则,nonceTtl 的值为 24 小时
|
||||
else {
|
||||
return 60 * 60 * 24;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSignConfig ["
|
||||
+ "secretKey=" + secretKey
|
||||
+ ", timestampDisparity=" + timestampDisparity
|
||||
+ ", isCheckNonce=" + isCheckNonce
|
||||
+ "]";
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Sa-Token 配置类 Model
|
||||
* <p>
|
||||
@ -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;
|
||||
|
||||
@ -105,7 +109,12 @@ public class SaTokenConfig implements Serializable {
|
||||
* Cookie配置对象
|
||||
*/
|
||||
public SaCookieConfig cookie = new SaCookieConfig();
|
||||
|
||||
|
||||
/**
|
||||
* API 签名配置对象
|
||||
*/
|
||||
public SaSignConfig sign = new SaSignConfig();
|
||||
|
||||
|
||||
/**
|
||||
* @return token名称 (同时也是cookie名称)
|
||||
@ -205,6 +214,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 +436,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;
|
||||
@ -512,7 +537,23 @@ public class SaTokenConfig implements Serializable {
|
||||
this.cookie = cookie;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return API 签名全局配置对象
|
||||
*/
|
||||
public SaSignConfig getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sign API 签名全局配置对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setSign(SaSignConfig sign) {
|
||||
this.sign = sign;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaTokenConfig ["
|
||||
@ -521,7 +562,8 @@ public class SaTokenConfig implements Serializable {
|
||||
+ ", activityTimeout=" + activityTimeout
|
||||
+ ", isConcurrent=" + isConcurrent
|
||||
+ ", isShare=" + isShare
|
||||
+ ", maxLoginCount=" + maxLoginCount
|
||||
+ ", maxLoginCount=" + maxLoginCount
|
||||
+ ", maxTryTimes=" + maxTryTimes
|
||||
+ ", isReadBody=" + isReadBody
|
||||
+ ", isReadHeader=" + isReadHeader
|
||||
+ ", isReadCookie=" + isReadCookie
|
||||
@ -540,69 +582,9 @@ public class SaTokenConfig implements Serializable {
|
||||
+ ", currDomain=" + currDomain
|
||||
+ ", sameTokenTimeout=" + sameTokenTimeout
|
||||
+ ", checkSameToken=" + checkSameToken
|
||||
+ ", cookie=" + cookie
|
||||
+ ", cookie=" + cookie
|
||||
+ ", sign=" + sign
|
||||
+ "]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import cn.dev33.satoken.error.SaErrorCode;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request 包装类
|
||||
* @author kong
|
||||
@ -70,8 +73,19 @@ public interface SaRequest {
|
||||
}
|
||||
return paramValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
public List<String> getParamNames();
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
public Map<String, String> getParamMap();
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
* @param name 键
|
||||
|
@ -0,0 +1,48 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 API 参数签名校验失败
|
||||
*
|
||||
* @author kong
|
||||
* @since 2023-5-3
|
||||
*/
|
||||
public class SaSignException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130144L;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 API 参数签名校验失败
|
||||
* @param message 异常描述
|
||||
*/
|
||||
public SaSignException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果flag==true,则抛出message异常
|
||||
* @param flag 标记
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void throwBy(boolean flag, String message) {
|
||||
if(flag) {
|
||||
throw new SaSignException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果 value isEmpty,则抛出 message 异常
|
||||
* @param value 值
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void throwByNull(Object value, String message) {
|
||||
if(SaFoxUtil.isEmpty(value)) {
|
||||
throw new SaSignException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
);
|
||||
|
||||
}
|
@ -262,12 +262,12 @@ public class SaTokenEventCenter {
|
||||
|
||||
/**
|
||||
* 全局组件载入
|
||||
* @param comtName 组件名称
|
||||
* @param comtObj 组件对象
|
||||
* @param compName 组件名称
|
||||
* @param compObj 组件对象
|
||||
*/
|
||||
public static void doRegisterComponent(String comtName, Object comtObj) {
|
||||
public static void doRegisterComponent(String compName, Object compObj) {
|
||||
for (SaTokenListener listener : listenerList) {
|
||||
listener.doRegisterComponent(comtName, comtObj);
|
||||
listener.doRegisterComponent(compName, compObj);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,10 +103,10 @@ public interface SaTokenListener {
|
||||
|
||||
/**
|
||||
* 全局组件载入
|
||||
* @param comtName 组件名称
|
||||
* @param comtObj 组件对象
|
||||
* @param compName 组件名称
|
||||
* @param compObj 组件对象
|
||||
*/
|
||||
public default void doRegisterComponent(String comtName, Object comtObj) {}
|
||||
public default void doRegisterComponent(String compName, Object compObj) {}
|
||||
|
||||
/**
|
||||
* StpLogic 对象替换
|
||||
|
@ -106,13 +106,13 @@ public class SaTokenListenerForLog implements SaTokenListener {
|
||||
|
||||
/**
|
||||
* 全局组件载入
|
||||
* @param comtName 组件名称
|
||||
* @param comtObj 组件对象
|
||||
* @param compName 组件名称
|
||||
* @param compObj 组件对象
|
||||
*/
|
||||
@Override
|
||||
public void doRegisterComponent(String comtName, Object comtObj) {
|
||||
String canonicalName = comtObj == null ? null : comtObj.getClass().getCanonicalName();
|
||||
log.info("全局组件 {} 载入成功: {}", comtName, canonicalName);
|
||||
public void doRegisterComponent(String compName, Object compObj) {
|
||||
String canonicalName = compObj == null ? null : compObj.getClass().getCanonicalName();
|
||||
log.info("全局组件 {} 载入成功: {}", compName, canonicalName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,33 +1,91 @@
|
||||
package cn.dev33.satoken.sign;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaSignConfig;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.error.SaErrorCode;
|
||||
import cn.dev33.satoken.exception.SaSignException;
|
||||
import cn.dev33.satoken.secure.SaSecureUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import cn.dev33.satoken.error.SaErrorCode;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.secure.SaSecureUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* 参数签名算法
|
||||
*
|
||||
* API 参数签名算法
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-27
|
||||
* @since 2022-4-27
|
||||
*/
|
||||
public interface SaSignTemplate {
|
||||
public class SaSignTemplate {
|
||||
|
||||
// ----------- 签名配置
|
||||
|
||||
SaSignConfig signConfig;
|
||||
|
||||
/**
|
||||
* 获取:API 签名配置
|
||||
* @return /
|
||||
*/
|
||||
public SaSignConfig getSignConfig() {
|
||||
return signConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:API 签名配置:
|
||||
* 1. 如果用户自定义了 signConfig ,则使用用户自定义的。
|
||||
* 2. 否则使用全局默认配置。
|
||||
* @return /
|
||||
*/
|
||||
public SaSignConfig getSignConfigOrGlobal() {
|
||||
// 如果用户自定义了 signConfig ,则使用用户自定义的
|
||||
if(signConfig != null) {
|
||||
return signConfig;
|
||||
}
|
||||
// 否则使用全局默认配置
|
||||
return SaManager.getConfig().getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:API 签名配置的秘钥
|
||||
* @return /
|
||||
*/
|
||||
public String getSecretKey() {
|
||||
return getSignConfigOrGlobal().getSecretKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置:API 签名配置
|
||||
* @param signConfig /
|
||||
*/
|
||||
public SaSignTemplate setSignConfig(SaSignConfig signConfig) {
|
||||
this.signConfig = signConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------- 自定义使用的参数名称 (不声明final,允许开发者自定义修改)
|
||||
|
||||
public static String key = "key";
|
||||
public static String timestamp = "timestamp";
|
||||
public static String nonce = "nonce";
|
||||
public static String sign = "sign";
|
||||
|
||||
|
||||
// ----------- 拼接参数
|
||||
|
||||
/**
|
||||
* 将所有参数连接成一个字符串(不排序),形如:b=28a=18c=3
|
||||
* @param paramsMap 参数列表
|
||||
* @return 拼接出的参数字符串
|
||||
*/
|
||||
public default String joinParams(Map<String, Object> paramsMap) {
|
||||
public String joinParams(Map<String, ?> paramsMap) {
|
||||
|
||||
// 按照 k1=v1&k2=v2&k3=v3 排列
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String key : paramsMap.keySet()) {
|
||||
Object value = paramsMap.get(key);
|
||||
if(SaFoxUtil.isEmpty(value) == false) {
|
||||
if( ! SaFoxUtil.isEmpty(value) ) {
|
||||
sb.append(key).append("=").append(value).append("&");
|
||||
}
|
||||
}
|
||||
@ -46,9 +104,9 @@ public interface SaSignTemplate {
|
||||
* @param paramsMap 参数列表
|
||||
* @return 拼接出的参数字符串
|
||||
*/
|
||||
public default String joinParamsDictSort(Map<String, Object> paramsMap) {
|
||||
public String joinParamsDictSort(Map<String, ?> paramsMap) {
|
||||
// 保证字段按照字典顺序排列
|
||||
if(paramsMap instanceof TreeMap == false) {
|
||||
if( ! (paramsMap instanceof TreeMap) ) {
|
||||
paramsMap = new TreeMap<>(paramsMap);
|
||||
}
|
||||
|
||||
@ -56,54 +114,40 @@ public interface SaSignTemplate {
|
||||
return joinParams(paramsMap);
|
||||
}
|
||||
|
||||
|
||||
// ----------- 创建签名
|
||||
|
||||
/**
|
||||
* 创建签名:md5(paramsStr + keyStr)
|
||||
* @param paramsMap 参数列表
|
||||
* @param key 秘钥
|
||||
* @param paramsMap 参数列表
|
||||
* @return 签名
|
||||
*/
|
||||
public default String createSign(Map<String, Object> paramsMap, String key) {
|
||||
SaTokenException.throwByNull(key, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201);
|
||||
|
||||
public String createSign(Map<String, ?> paramsMap) {
|
||||
String secretKey = getSecretKey();
|
||||
SaSignException.throwByNull(secretKey, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201);
|
||||
|
||||
// 如果调用者不小心传入了 sign 参数,则此处需要将 sign 参数排除在外
|
||||
if(paramsMap.containsKey(sign)) {
|
||||
// 为了保证不影响原有的 paramsMap,此处需要再复制一份
|
||||
paramsMap = new TreeMap<>(paramsMap);
|
||||
paramsMap.remove(sign);
|
||||
}
|
||||
|
||||
// 计算签名
|
||||
String paramsStr = joinParamsDictSort(paramsMap);
|
||||
String fullStr = paramsStr + "&key=" + key;
|
||||
String fullStr = paramsStr + "&" + key + "=" + secretKey;
|
||||
return SaSecureUtil.md5(fullStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:给定的参数 + 秘钥 生成的签名是否为有效签名
|
||||
* @param paramsMap 参数列表
|
||||
* @param key 秘钥
|
||||
* @param sign 待验证的签名
|
||||
* @return 签名是否有效
|
||||
*/
|
||||
public default boolean isValidSign(Map<String, Object> paramsMap, String key, String sign) {
|
||||
String theSign = createSign(paramsMap, key);
|
||||
return theSign.equals(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:给定的参数 + 秘钥 生成的签名是否为有效签名,如果签名无效则抛出异常
|
||||
* @param paramsMap 参数列表
|
||||
* @param key 秘钥
|
||||
* @param sign 待验证的签名
|
||||
*/
|
||||
public default void checkSign(Map<String, Object> paramsMap, String key, String sign) {
|
||||
if(isValidSign(paramsMap, key, sign) == false) {
|
||||
throw new SaTokenException("无效签名:" + sign).setCode(SaErrorCode.CODE_12202);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 paramsMap 追加 timestamp、nonce、sign 三个参数
|
||||
* @param paramsMap 参数列表
|
||||
* @param key 秘钥
|
||||
* @param paramsMap 参数列表
|
||||
* @return 加工后的参数列表
|
||||
*/
|
||||
public default Map<String, Object> addSignParams(Map<String, Object> paramsMap, String key) {
|
||||
paramsMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
||||
paramsMap.put("nonce", SaFoxUtil.getRandomString(32));
|
||||
paramsMap.put("sign", createSign(paramsMap, key));
|
||||
public Map<String, Object> addSignParams(Map<String, Object> paramsMap) {
|
||||
paramsMap.put(timestamp, String.valueOf(System.currentTimeMillis()));
|
||||
paramsMap.put(nonce, SaFoxUtil.getRandomString(32));
|
||||
paramsMap.put(sign, createSign(paramsMap));
|
||||
return paramsMap;
|
||||
}
|
||||
|
||||
@ -111,24 +155,26 @@ public interface SaSignTemplate {
|
||||
* 给 paramsMap 追加 timestamp、nonce、sign 三个参数,并转换为参数字符串,形如:
|
||||
* <code>data=xxx8nonce=xxx8timestamp=xxx8sign=xxx</code>
|
||||
* @param paramsMap 参数列表
|
||||
* @param key 秘钥
|
||||
* @return 加工后的参数列表 转化为的参数字符串
|
||||
* @return 加工后的参数列表 转化为的参数字符串
|
||||
*/
|
||||
public default String addSignParamsToString(Map<String, Object> paramsMap, String key) {
|
||||
// 追加参数
|
||||
paramsMap = addSignParams(paramsMap, key);
|
||||
|
||||
// .
|
||||
return joinParams(paramsMap);
|
||||
public String addSignParamsAndJoin(Map<String, Object> paramsMap) {
|
||||
// 追加参数
|
||||
paramsMap = addSignParams(paramsMap);
|
||||
|
||||
// 拼接参数
|
||||
return joinParams(paramsMap);
|
||||
}
|
||||
|
||||
|
||||
// ----------- 校验签名
|
||||
|
||||
/**
|
||||
* 判断:指定时间戳与当前时间戳的差距是否在允许的范围内
|
||||
* @param timestamp 待校验的时间戳
|
||||
* @param allowDisparity 允许的最大时间差(单位:ms),-1 代表不限制
|
||||
* @return 是否在允许的范围内
|
||||
*/
|
||||
public default boolean isValidTimestamp(long timestamp, long allowDisparity) {
|
||||
public boolean isValidTimestamp(long timestamp) {
|
||||
long allowDisparity = getSignConfigOrGlobal().getTimestampDisparity();
|
||||
long disparity = Math.abs(System.currentTimeMillis() - timestamp);
|
||||
return allowDisparity == -1 || disparity <= allowDisparity;
|
||||
}
|
||||
@ -136,12 +182,142 @@ public interface SaSignTemplate {
|
||||
/**
|
||||
* 校验:指定时间戳与当前时间戳的差距是否在允许的范围内,如果超出则抛出异常
|
||||
* @param timestamp 待校验的时间戳
|
||||
* @param allowDisparity 允许的最大时间差(单位:ms),-1 代表不限制
|
||||
*/
|
||||
public default void checkTimestamp(long timestamp, long allowDisparity) {
|
||||
if(isValidTimestamp(timestamp, allowDisparity) == false) {
|
||||
throw new SaTokenException("timestamp 超出允许的范围:" + timestamp).setCode(SaErrorCode.CODE_12203);
|
||||
public void checkTimestamp(long timestamp) {
|
||||
if( ! isValidTimestamp(timestamp) ) {
|
||||
throw new SaSignException("timestamp 超出允许的范围:" + timestamp).setCode(SaErrorCode.CODE_12203);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:随机字符串 nonce 是否有效。
|
||||
* 注意:同一 nonce 可以被多次判断有效,不会被缓存
|
||||
* @param nonce 待判断的随机字符串
|
||||
* @return 是否有效
|
||||
*/
|
||||
public boolean isValidNonce(String nonce) {
|
||||
// 为空代表无效
|
||||
if(SaFoxUtil.isEmpty(nonce)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验此 nonce 是否已被使用过
|
||||
String key = splicingNonceSaveKey(nonce);
|
||||
return SaManager.getSaTokenDao().get(key) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:随机字符串 nonce 是否有效,如果无效则抛出异常。
|
||||
* 注意:同一 nonce 只可以被校验通过一次,校验后将保存在缓存中,再次校验将无法通过
|
||||
* @param nonce 待校验的随机字符串
|
||||
*/
|
||||
public void checkNonce(String nonce) {
|
||||
// 为空代表无效
|
||||
if(SaFoxUtil.isEmpty(nonce)) {
|
||||
throw new SaSignException("nonce 为空,无效");
|
||||
}
|
||||
|
||||
// 校验此 nonce 是否已被使用过
|
||||
String key = splicingNonceSaveKey(nonce);
|
||||
if(SaManager.getSaTokenDao().get(key) != null) {
|
||||
throw new SaSignException("此 nonce 已被使用过,不可重复使用:" + nonce);
|
||||
}
|
||||
|
||||
// 校验通过后,将此 nonce 保存在缓存中,保证下次校验无法通过
|
||||
SaManager.getSaTokenDao().set(key, nonce, getSignConfigOrGlobal().getSaveNonceExpire());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:给定的参数 + 秘钥 生成的签名是否为有效签名
|
||||
* @param paramsMap 参数列表
|
||||
* @param sign 待验证的签名
|
||||
* @return 签名是否有效
|
||||
*/
|
||||
public boolean isValidSign(Map<String, ?> paramsMap, String sign) {
|
||||
String theSign = createSign(paramsMap);
|
||||
return theSign.equals(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:给定的参数 + 秘钥 生成的签名是否为有效签名,如果签名无效则抛出异常
|
||||
* @param paramsMap 参数列表
|
||||
* @param sign 待验证的签名
|
||||
*/
|
||||
public void checkSign(Map<String, ?> paramsMap, String sign) {
|
||||
if( ! isValidSign(paramsMap, sign) ) {
|
||||
throw new SaSignException("无效签名:" + sign).setCode(SaErrorCode.CODE_12202);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:参数列表中的 nonce、timestamp、sign 是否均为合法的
|
||||
* @param paramMap 待校验的请求参数集合
|
||||
* @return 是否合法
|
||||
*/
|
||||
public boolean isValidParamMap(Map<String, String> paramMap) {
|
||||
// 获取必须的三个参数
|
||||
String timestampValue = paramMap.get(timestamp);
|
||||
String nonceValue = paramMap.get(nonce);
|
||||
String signValue = paramMap.get(sign);
|
||||
|
||||
// 三个参数必须全部非空
|
||||
SaSignException.throwByNull(timestampValue, "缺少 timestamp 字段");
|
||||
SaSignException.throwByNull(nonceValue, "缺少 nonce 字段");
|
||||
SaSignException.throwByNull(signValue, "缺少 sign 字段");
|
||||
|
||||
// 三个值的校验必须全部通过
|
||||
return isValidTimestamp(Long.parseLong(timestampValue))
|
||||
&& (getSignConfigOrGlobal().getIsCheckNonce() ? isValidNonce(nonceValue) : true)
|
||||
&& isValidSign(paramMap, signValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:参数列表中的 nonce、timestamp、sign 是否均为合法的,如果不合法,则抛出对应的异常
|
||||
* @param paramMap 待校验的请求参数集合
|
||||
*/
|
||||
public void checkParamMap(Map<String, String> paramMap) {
|
||||
// 获取必须的三个参数
|
||||
String timestampValue = paramMap.get(timestamp);
|
||||
String nonceValue = paramMap.get(nonce);
|
||||
String signValue = paramMap.get(sign);
|
||||
|
||||
// 依次校验三个参数
|
||||
checkTimestamp(Long.parseLong(timestampValue));
|
||||
if(getSignConfigOrGlobal().getIsCheckNonce()) {
|
||||
checkNonce(nonceValue);
|
||||
}
|
||||
checkSign(paramMap, signValue);
|
||||
|
||||
// 通过 √
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:一个请求中的 nonce、timestamp、sign 是否均为合法的
|
||||
* @param request 待校验的请求对象
|
||||
* @return 是否合法
|
||||
*/
|
||||
public boolean isValidRequest(SaRequest request) {
|
||||
return isValidParamMap(request.getParamMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:一个请求的 nonce、timestamp、sign 是否均为合法的,如果不合法,则抛出对应的异常
|
||||
* @param request 待校验的请求对象
|
||||
*/
|
||||
public void checkRequest(SaRequest request) {
|
||||
checkParamMap(request.getParamMap());
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 返回相应key -------------------
|
||||
|
||||
/**
|
||||
* 拼接key:存储 nonce 时使用的 key
|
||||
* @param nonce nonce 值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingNonceSaveKey(String nonce) {
|
||||
return SaManager.getConfig().getTokenName() + ":sign:nonce:" + nonce;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
package cn.dev33.satoken.sign;
|
||||
|
||||
/**
|
||||
* 参数签名算法 [默认实现类]
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-27
|
||||
*/
|
||||
public class SaSignTemplateDefaultImpl implements SaSignTemplate {
|
||||
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
package cn.dev33.satoken.sign;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API 参数签名算法 - 工具类
|
||||
*
|
||||
* @author kong
|
||||
* @since 2022-4-27
|
||||
*/
|
||||
public class SaSignUtil {
|
||||
|
||||
// ----------- 拼接参数
|
||||
|
||||
/**
|
||||
* 将所有参数连接成一个字符串(不排序),形如:b=28a=18c=3
|
||||
* @param paramsMap 参数列表
|
||||
* @return 拼接出的参数字符串
|
||||
*/
|
||||
public static String joinParams(Map<String, ?> paramsMap) {
|
||||
return SaManager.getSaSignTemplate().joinParams(paramsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将所有参数按照字典顺序连接成一个字符串,形如:a=18b=28c=3
|
||||
* @param paramsMap 参数列表
|
||||
* @return 拼接出的参数字符串
|
||||
*/
|
||||
public static String joinParamsDictSort(Map<String, ?> paramsMap) {
|
||||
return SaManager.getSaSignTemplate().joinParamsDictSort(paramsMap);
|
||||
}
|
||||
|
||||
|
||||
// ----------- 创建签名
|
||||
|
||||
/**
|
||||
* 创建签名:md5(paramsStr + keyStr)
|
||||
* @param paramsMap 参数列表
|
||||
* @return 签名
|
||||
*/
|
||||
public static String createSign(Map<String, ?> paramsMap) {
|
||||
return SaManager.getSaSignTemplate().createSign(paramsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 paramsMap 追加 timestamp、nonce、sign 三个参数
|
||||
* @param paramsMap 参数列表
|
||||
* @return 加工后的参数列表
|
||||
*/
|
||||
public static Map<String, Object> addSignParams(Map<String, Object> paramsMap) {
|
||||
return SaManager.getSaSignTemplate().addSignParams(paramsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 paramsMap 追加 timestamp、nonce、sign 三个参数,并转换为参数字符串,形如:
|
||||
* <code>data=xxx8nonce=xxx8timestamp=xxx8sign=xxx</code>
|
||||
* @param paramsMap 参数列表
|
||||
* @return 加工后的参数列表 转化为的参数字符串
|
||||
*/
|
||||
public static String addSignParamsAndJoin(Map<String, Object> paramsMap) {
|
||||
return SaManager.getSaSignTemplate().addSignParamsAndJoin(paramsMap);
|
||||
}
|
||||
|
||||
|
||||
// ----------- 校验签名
|
||||
|
||||
/**
|
||||
* 判断:指定时间戳与当前时间戳的差距是否在允许的范围内
|
||||
* @param timestamp 待校验的时间戳
|
||||
* @return 是否在允许的范围内
|
||||
*/
|
||||
public static boolean isValidTimestamp(long timestamp) {
|
||||
return SaManager.getSaSignTemplate().isValidTimestamp(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:指定时间戳与当前时间戳的差距是否在允许的范围内,如果超出则抛出异常
|
||||
* @param timestamp 待校验的时间戳
|
||||
*/
|
||||
public static void checkTimestamp(long timestamp) {
|
||||
SaManager.getSaSignTemplate().checkTimestamp(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:随机字符串 nonce 是否有效。
|
||||
* 注意:同一 nonce 可以被多次判断有效,不会被缓存
|
||||
* @param nonce 待判断的随机字符串
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean isValidNonce(String nonce) {
|
||||
return SaManager.getSaSignTemplate().isValidNonce(nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:随机字符串 nonce 是否有效,如果无效则抛出异常。
|
||||
* 注意:同一 nonce 只可以被校验通过一次,校验后将保存在缓存中,再次校验将无法通过
|
||||
* @param nonce 待校验的随机字符串
|
||||
*/
|
||||
public static void checkNonce(String nonce) {
|
||||
SaManager.getSaSignTemplate().checkNonce(nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:给定的参数 + 秘钥 生成的签名是否为有效签名
|
||||
* @param paramsMap 参数列表
|
||||
* @param sign 待验证的签名
|
||||
* @return 签名是否有效
|
||||
*/
|
||||
public static boolean isValidSign(Map<String, ?> paramsMap, String sign) {
|
||||
return SaManager.getSaSignTemplate().isValidSign(paramsMap, sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:给定的参数 + 秘钥 生成的签名是否为有效签名,如果签名无效则抛出异常
|
||||
* @param paramsMap 参数列表
|
||||
* @param sign 待验证的签名
|
||||
*/
|
||||
public static void checkSign(Map<String, ?> paramsMap, String sign) {
|
||||
SaManager.getSaSignTemplate().checkSign(paramsMap, sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:参数列表中的 nonce、timestamp、sign 是否均为合法的
|
||||
* @param paramMap 待校验的请求参数集合
|
||||
* @return 是否合法
|
||||
*/
|
||||
public static boolean isValidParamMap(Map<String, String> paramMap) {
|
||||
return SaManager.getSaSignTemplate().isValidParamMap(paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:参数列表中的 nonce、timestamp、sign 是否均为合法的,如果不合法,则抛出对应的异常
|
||||
* @param paramMap 待校验的请求参数集合
|
||||
*/
|
||||
public static void checkParamMap(Map<String, String> paramMap) {
|
||||
SaManager.getSaSignTemplate().checkParamMap(paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:一个请求中的 nonce、timestamp、sign 是否均为合法的
|
||||
* @param request 待校验的请求对象
|
||||
* @return 是否合法
|
||||
*/
|
||||
public static boolean isValidRequest(SaRequest request) {
|
||||
return SaManager.getSaSignTemplate().isValidRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:一个请求的 nonce、timestamp、sign 是否均为合法的,如果不合法,则抛出对应的异常
|
||||
* @param request 待校验的请求对象
|
||||
*/
|
||||
public static void checkRequest(SaRequest request) {
|
||||
SaManager.getSaSignTemplate().checkRequest(request);
|
||||
}
|
||||
|
||||
}
|
@ -351,7 +351,7 @@ public class StpLogic {
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public String createLoginSession(Object id, SaLoginModel loginModel) {
|
||||
|
||||
|
||||
// ------ 前置检查
|
||||
SaTokenException.throwByNull(id, "账号id不能为空", SaErrorCode.CODE_11002);
|
||||
|
||||
@ -395,7 +395,7 @@ public class StpLogic {
|
||||
* @return 返回 Token
|
||||
*/
|
||||
protected String distUsableToken(Object id, SaLoginModel loginModel) {
|
||||
|
||||
|
||||
// 获取全局配置
|
||||
Boolean isConcurrent = getConfig().getIsConcurrent();
|
||||
|
||||
@ -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
|
||||
@ -1382,7 +1401,15 @@ public class StpLogic {
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public void checkRoleAnd(String... roleArray){
|
||||
Object loginId = getLoginId();
|
||||
// 先获取当前是哪个账号id
|
||||
Object loginId = getLoginId();
|
||||
|
||||
// 如果没有指定权限,那么直接跳过
|
||||
if(roleArray == null || roleArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始校验
|
||||
List<String> roleList = getRoleList(loginId);
|
||||
for (String role : roleArray) {
|
||||
if(!hasElement(roleList, role)) {
|
||||
@ -1396,7 +1423,15 @@ public class StpLogic {
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public void checkRoleOr(String... roleArray){
|
||||
Object loginId = getLoginId();
|
||||
// 先获取当前是哪个账号id
|
||||
Object loginId = getLoginId();
|
||||
|
||||
// 如果没有指定权限,那么直接跳过
|
||||
if(roleArray == null || roleArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始校验
|
||||
List<String> roleList = getRoleList(loginId);
|
||||
for (String role : roleArray) {
|
||||
if(hasElement(roleList, role)) {
|
||||
@ -1404,9 +1439,9 @@ public class StpLogic {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(roleArray.length > 0) {
|
||||
throw new NotRoleException(roleArray[0], this.loginType).setCode(SaErrorCode.CODE_11041);
|
||||
}
|
||||
|
||||
// 代码至此,说明一个都没通过,需要抛出无角色异常
|
||||
throw new NotRoleException(roleArray[0], this.loginType).setCode(SaErrorCode.CODE_11041);
|
||||
}
|
||||
|
||||
|
||||
@ -1495,7 +1530,15 @@ public class StpLogic {
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public void checkPermissionAnd(String... permissionArray){
|
||||
Object loginId = getLoginId();
|
||||
// 先获取当前是哪个账号id
|
||||
Object loginId = getLoginId();
|
||||
|
||||
// 如果没有指定权限,那么直接跳过
|
||||
if(permissionArray == null || permissionArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始校验
|
||||
List<String> permissionList = getPermissionList(loginId);
|
||||
for (String permission : permissionArray) {
|
||||
if(!hasElement(permissionList, permission)) {
|
||||
@ -1509,7 +1552,15 @@ public class StpLogic {
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public void checkPermissionOr(String... permissionArray){
|
||||
Object loginId = getLoginId();
|
||||
// 先获取当前是哪个账号id
|
||||
Object loginId = getLoginId();
|
||||
|
||||
// 如果没有指定权限,那么直接跳过
|
||||
if(permissionArray == null || permissionArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始校验
|
||||
List<String> permissionList = getPermissionList(loginId);
|
||||
for (String permission : permissionArray) {
|
||||
if(hasElement(permissionList, permission)) {
|
||||
@ -1517,9 +1568,9 @@ public class StpLogic {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(permissionArray.length > 0) {
|
||||
throw new NotPermissionException(permissionArray[0], this.loginType).setCode(SaErrorCode.CODE_11051);
|
||||
}
|
||||
|
||||
// 代码至此,说明一个都没通过,需要抛出无权限异常
|
||||
throw new NotPermissionException(permissionArray[0], this.loginType).setCode(SaErrorCode.CODE_11051);
|
||||
}
|
||||
|
||||
|
||||
@ -2273,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 内部工具类
|
||||
*
|
||||
|
@ -94,7 +94,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -42,7 +42,7 @@
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
@ -50,7 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
</dependency>
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
|
@ -13,7 +13,7 @@
|
||||
<version>2.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.34.0</sa-token.version>
|
||||
|
@ -42,7 +42,7 @@ public class SaTokenConfigure {
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -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
|
||||
|
@ -26,7 +26,7 @@ public class GlobalException {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
|
@ -1,14 +1,13 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
|
||||
/**
|
||||
@ -24,7 +23,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册 Sa-Token 拦截器打开注解鉴权功能
|
||||
// 注册 Sa-Token 拦截器打开注解鉴权功能
|
||||
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
@ -40,7 +39,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
// 认证函数: 每次请求执行
|
||||
.setAuth(obj -> {
|
||||
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
|
||||
// SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
|
||||
|
||||
})
|
||||
|
||||
@ -50,7 +49,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行 (BeforeAuth不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -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,8 +18,8 @@ sa-token:
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
@ -50,7 +50,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -41,7 +41,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
// 认证函数: 每次请求执行
|
||||
.setAuth(obj -> {
|
||||
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
|
||||
// SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
|
||||
|
||||
})
|
||||
|
||||
@ -51,7 +51,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -51,7 +51,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -1,17 +1,17 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.sign.SaSignUtil;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.dtflys.forest.Forest;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
* @author kong
|
||||
@ -63,5 +63,23 @@ public class SsoServerController {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 示例:获取数据接口(用于在模式三下,为 client 端开放拉取数据的接口)
|
||||
@RequestMapping("/sso/getData")
|
||||
public Object getData(String apiType, String loginId) {
|
||||
System.out.println("---------------- 获取数据 ----------------");
|
||||
System.out.println("apiType=" + apiType);
|
||||
System.out.println("loginId=" + loginId);
|
||||
|
||||
// 校验签名:只有拥有正确秘钥发起的请求才能通过校验
|
||||
SaSignUtil.checkRequest(SaHolder.getRequest());
|
||||
|
||||
// 自定义返回结果(模拟)
|
||||
return SaResult.ok()
|
||||
.set("id", loginId)
|
||||
.set("name", "LinXiaoYu")
|
||||
.set("sex", "女")
|
||||
.set("age", 18);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,14 +15,14 @@ sa-token:
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
allow-url: "*"
|
||||
# 是否打开单点注销功能
|
||||
is-slo: true
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开)
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
|
||||
# 是否打开模式三
|
||||
isHttp: true
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
is-http: true
|
||||
sign:
|
||||
# API 接口调用秘钥
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
|
||||
spring:
|
||||
|
@ -1,17 +1,18 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dtflys.forest.Forest;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
@ -52,11 +53,17 @@ public class SsoClientController {
|
||||
}
|
||||
|
||||
// 查询我的账号信息
|
||||
@RequestMapping("/sso/myinfo")
|
||||
public Object myinfo() {
|
||||
Object userinfo = SaSsoUtil.getUserinfo(StpUtil.getLoginId());
|
||||
System.out.println("--------info:" + userinfo);
|
||||
return userinfo;
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 组织请求参数
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("apiType", "userinfo");
|
||||
map.put("loginId", StpUtil.getLoginId());
|
||||
|
||||
// 发起请求
|
||||
Object resData = SaSsoUtil.getData(map);
|
||||
System.out.println("sso-server 返回的信息:" + resData);
|
||||
return resData;
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
|
@ -8,19 +8,18 @@ sa-token:
|
||||
sso:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
# 使用Http请求校验ticket
|
||||
# 使用 Http 请求校验ticket (模式三)
|
||||
is-http: true
|
||||
# SSO-Server端 ticket校验地址
|
||||
check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket
|
||||
# 打开单点注销功能
|
||||
is-slo: true
|
||||
# 单点注销地址
|
||||
slo-url: http://sa-sso-server.com:9000/sso/signout
|
||||
# 接口调用秘钥
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# SSO-Server端 查询userinfo地址
|
||||
userinfo-url: http://sa-sso-server.com:9000/sso/userinfo
|
||||
|
||||
# 查询数据地址
|
||||
get-data-url: http://sa-sso-server.com:9000/sso/getData
|
||||
sign:
|
||||
# API 接口调用秘钥
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
spring:
|
||||
# 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis)
|
||||
redis:
|
||||
|
@ -428,6 +428,12 @@ Caused by: java.lang.ClassNotFoundException: cn.dev33.satoken.same.SaSameTemplat
|
||||
- (2) 在自定义StpUtil类加上类似 @Component 的注解让容器启动时扫描到自动初始化
|
||||
|
||||
|
||||
### Q:使用拦截器鉴权,访问一个不存在的 path 时,springboot 会自动在控制台打印一下异常。
|
||||
可尝试添加以下配置解决:
|
||||
``` properties
|
||||
spring.resources.add-mappings=false
|
||||
spring.mvc.throw-exception-if-no-handler-found=true
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -470,6 +476,30 @@ public class GlobalExceptionHandler {
|
||||
}
|
||||
```
|
||||
|
||||
### Q:在 SaInterceptor 中,注解鉴权总是先于路由拦截鉴权执行,能调整一下顺序吗?
|
||||
框架没有提供直接的 API,但你有以下两种方式可以做到这一点:
|
||||
- 方式1:将 SaInterceptor 里的代码复制出来一份,按照你的需求改一下,然后使用你这个自定义的拦截器,不再使用官方的。
|
||||
- 方式2:注册两次 SaInterceptor 拦截器,例如:
|
||||
|
||||
``` java
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 路由拦截鉴权
|
||||
registry.addInterceptor(new SaInterceptor(r -> {
|
||||
// 路由拦截鉴权的代码 ...
|
||||
}).isAnnotation(false)).addPathPatterns("/**");
|
||||
|
||||
// 打开注解鉴权
|
||||
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
}
|
||||
```
|
||||
如上,第一个完成路由拦截鉴权功能,第二个完成注解鉴权功能。
|
||||
|
||||
|
||||
### Q:我的项目权限模型不是RBAC模型,很复杂,可以集成吗?
|
||||
无论什么模型,只要能把一个用户具有的所有权限塞到一个List里返回给框架,就能集成
|
||||
|
||||
|
@ -234,6 +234,12 @@ public void setSaJwtTemplate() {
|
||||
技术上来讲无法将其踢下线,所以此时顶人下线和踢人下线等 API 都属于不可用状态,所以此时 `is-concurrent` 配置项必须配置为 `true`。
|
||||
|
||||
|
||||
##### 3、使用 jwt-mixin 模式后,max-try-times 恒等于 -1。
|
||||
|
||||
为防止框架错误判断 token 唯一性,当使用 jwt-mixin 模式后,`max-try-times` 恒等于 -1。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -57,6 +57,9 @@ public class SaSsoServerApplication {
|
||||
解决方案:在 sso-client 也新建上这个类,而且包名需要与 sso-server 端的一致(直接从 sso-server 把实体类复制过来就好了)
|
||||
|
||||
|
||||
### 模式三配置一堆 xxx-url ,有办法简化一下吗?
|
||||
可以使用 `sa-token.sso.server-url` 配置项来简化,参考:[配置项详解:serverurl](/use/config?id=配置项详解:serverurl)
|
||||
|
||||
|
||||
### 问:SSO模式二或模式三,第一个 client 登录成功之后再访问其它两个 client 不会自动登录,需要点一下登录按钮才会登录上?
|
||||
答:这是正常现象,系统 1 登录成功之后,系统 2 与系统 3 需要点击登录按钮,才会登录成功。
|
||||
@ -184,6 +187,13 @@ public class SsoUserServerController {
|
||||
public StpLogic getStpLogic() {
|
||||
return StpUserUtil.stpLogic;
|
||||
}
|
||||
// 使用自定义的签名秘钥
|
||||
SaSignConfig signConfig = new SaSignConfig().setSecretKey("xxxx-新的秘钥-xxxx");
|
||||
SaSignTemplate userSignTemplate = new SaSignTemplate().setSignConfig(signConfig);
|
||||
@Override
|
||||
public SaSignTemplate getSignTemplate() {
|
||||
return userSignTemplate;
|
||||
}
|
||||
};
|
||||
// 让这个SSO请求处理器,使用的路由前缀是 /sso-user,而不是原先的 /sso
|
||||
ssoUserTemplate.apiName.replacePrefix("/sso-user");
|
||||
|
@ -178,14 +178,13 @@ sa-token:
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
allow-url: "*"
|
||||
# 是否打开单点注销功能
|
||||
is-slo: true
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开)
|
||||
# ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
|
||||
# 是否打开模式三
|
||||
isHttp: true
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
is-http: true
|
||||
sign:
|
||||
# API 接口调用秘钥
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
|
||||
spring:
|
||||
@ -219,14 +218,12 @@ server.port=9000
|
||||
sa-token.sso.ticket-timeout=300
|
||||
# 所有允许的授权回调地址
|
||||
sa-token.sso.allow-url=*
|
||||
# 是否打开单点注销功能
|
||||
sa-token.sso.is-slo=true
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开)
|
||||
# ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
|
||||
# 是否打开模式三
|
||||
sa-token.sso.isHttp=true
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
sa-token.sso.secretkey=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
sa-token.sso.is-http=true
|
||||
# API 接口调用秘钥
|
||||
sa-token.sign.secret-key=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
|
||||
|
@ -173,8 +173,6 @@ sa-token:
|
||||
sso:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
# 是否打开单点注销接口
|
||||
is-slo: true
|
||||
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
alone-redis:
|
||||
@ -197,8 +195,6 @@ server.port=9001
|
||||
######### Sa-Token 配置 #########
|
||||
# SSO-Server端 统一认证地址
|
||||
sa-token.sso.auth-url=http://sa-sso-server.com:9000/sso/auth
|
||||
# 是否打开单点注销接口
|
||||
sa-token.sso.is-slo=true
|
||||
|
||||
# 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
# Redis数据库索引
|
||||
|
@ -98,109 +98,160 @@ forest.log-enabled: false
|
||||
> 注:如果已测试运行模式二,可先将Redis中的数据清空,以防旧数据对测试造成干扰
|
||||
|
||||
|
||||
### 3、获取 Userinfo
|
||||
除了账号id,我们可能还需要将用户的昵称、头像等信息从 Server端 带到 Client端,即:用户资料的同步。
|
||||
### 3、获取 UserInfo
|
||||
除了账号id,我们可能还需要将用户的昵称、头像等信息从 Server端 带到 Client端,即:用户资料的拉取。
|
||||
|
||||
在模式二中我们只需要将需要同步的资料放到 SaSession 即可,但是在模式三中两端不再连接同一个Redis,这时候我们需要通过http接口来同步信息:
|
||||
在模式二中我们只需要将需要同步的资料放到 SaSession 即可,但是在模式三中两端不再连接同一个 Redis,这时候我们需要通过 http 接口来同步信息。
|
||||
|
||||
在旧版本`(<= v1.34.0)` 框架提供的方案是配置 getUserinfo 接口地址,从 client 调用拉取数据,该方案有以下缺点:
|
||||
- 每次调用只能传递固定 loginId 一个参数,不方便。
|
||||
- 只能拉取 userinfo 数据,不通用。
|
||||
- 如果还需要拉取其它业务数据,需要再自定义一个接口,比较麻烦。
|
||||
|
||||
为此,我们设计了更通用、灵活的 getData 接口,解决上述三个难题。
|
||||
|
||||
#### 3.1、首先在 Server 端开放一个查询数据的接口
|
||||
|
||||
#### 3.1、在 Server 端自定义接口,查询用户资料
|
||||
``` java
|
||||
// 自定义接口:获取userinfo
|
||||
@RequestMapping("/sso/userinfo")
|
||||
public Object userinfo(String loginId) {
|
||||
System.out.println("---------------- 获取userinfo --------");
|
||||
|
||||
// 校验签名,防止敏感信息外泄
|
||||
SaSsoUtil.checkSign(SaHolder.getRequest());
|
||||
// 示例:获取数据接口(用于在模式三下,为 client 端开放拉取数据的接口)
|
||||
@RequestMapping("/sso/getData")
|
||||
public Object getData(String apiType, String loginId) {
|
||||
System.out.println("---------------- 获取数据 ----------------");
|
||||
System.out.println("apiType=" + apiType);
|
||||
System.out.println("loginId=" + loginId);
|
||||
|
||||
// 校验签名:只有拥有正确秘钥发起的请求才能通过校验
|
||||
SaSignUtil.checkRequest(SaHolder.getRequest());
|
||||
|
||||
// 自定义返回结果(模拟)
|
||||
return SaResult.ok()
|
||||
.set("id", loginId)
|
||||
.set("name", "linxiaoyu")
|
||||
.set("name", "LinXiaoYu")
|
||||
.set("sex", "女")
|
||||
.set("age", 18);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2、在 Client 端调用此接口查询 userinfo
|
||||
#### 3.2、在 Client 端调用此接口查询数据
|
||||
|
||||
首先在 application.yml 中配置接口地址:
|
||||
<!---------------------------- tabs:start ---------------------------->
|
||||
<!------------- tab:yaml 风格 ------------->
|
||||
``` yaml
|
||||
sa-token:
|
||||
sso:
|
||||
# SSO-Server端 查询userinfo地址
|
||||
userinfo-url: http://sa-sso-server.com:9000/sso/userinfo
|
||||
# sso-server 端拉取数据地址
|
||||
get-data-url: http://sa-sso-server.com:9000/sso/getData
|
||||
```
|
||||
<!------------- tab:properties 风格 ------------->
|
||||
``` properties
|
||||
# SSO-Server端 查询userinfo地址
|
||||
sa-token.sso.userinfo-url=http://sa-sso-server.com:9000/sso/userinfo
|
||||
# sso-server 端拉取数据地址
|
||||
sa-token.sso.get-data-url=http://sa-sso-server.com:9000/sso/getData
|
||||
```
|
||||
<!---------------------------- tabs:end ---------------------------->
|
||||
|
||||
|
||||
|
||||
然后在`SsoClientController`中新增接口
|
||||
然后在 `SsoClientController` 中新增接口
|
||||
``` java
|
||||
// 查询我的账号信息
|
||||
@RequestMapping("/sso/myinfo")
|
||||
public Object myinfo() {
|
||||
Object userinfo = SaSsoUtil.getUserinfo(StpUtil.getLoginId());
|
||||
System.out.println("--------info:" + userinfo);
|
||||
return userinfo;
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 组织请求参数
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("apiType", "userinfo");
|
||||
map.put("loginId", StpUtil.getLoginId());
|
||||
|
||||
// 发起请求
|
||||
Object resData = SaSsoUtil.getData(map);
|
||||
System.out.println("sso-server 返回的信息:" + resData);
|
||||
return resData;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3、访问测试
|
||||
访问测试:[http://sa-sso-client1.com:9001/sso/myinfo](http://sa-sso-client1.com:9001/sso/myinfo)
|
||||
访问测试:[http://sa-sso-client1.com:9001/sso/myInfo](http://sa-sso-client1.com:9001/sso/myInfo)
|
||||
|
||||
|
||||
### 4、自定义接口通信
|
||||
|
||||
群里有小伙伴提问:`SaSsoUtil.getUserinfo` 提供的参数太少,只有一个 loginId,无法满足业务需求怎么办?
|
||||
上述示例展示在 client 端向 server 拉取 userinfo 数据的步骤,如果你还需要拉取其它业务的数据,稍加改造示例便可以实现。
|
||||
|
||||
答:SaSsoUtil.getUserinfo只是为了避免你在项目中硬编码认证中心 url 而提供的简易封装,如果这个API无法满足你的业务需求,
|
||||
你完全可以在 Server 端自定义一些接口然后从 Client 端使用 http 工具调用即可。
|
||||
#### 4.1、方式一,使用 apiType 参数来区分业务
|
||||
|
||||
以下是一个简单的示例:
|
||||
我们可以约定好,使用 apiType 来区分不同的业务,例如:
|
||||
- 当 `apiType=userinfo` 时:代表拉取用户资料。
|
||||
- 当 `apiType=followList` 时:代表拉取用户的关注列表。
|
||||
- 当 `apiType=fansList` 时:代表拉取用户的粉丝列表。
|
||||
|
||||
#### 4.1、先在 sso-server 端自定义一个接口
|
||||
``` java
|
||||
// 获取指定用户的关注列表
|
||||
@RequestMapping("/sso/getFollowList")
|
||||
public Object ssoRequest(Long loginId) {
|
||||
此时,我们便可以通过在 client 端传入不同的 apiType 参数,来区分不同的业务。
|
||||
|
||||
// 校验签名,签名不通过直接抛出异常
|
||||
SaSsoUtil.checkSign(SaHolder.getRequest());
|
||||
|
||||
// 查询数据 (此处仅做模拟)
|
||||
List<Integer> list = Arrays.asList(10041, 10042, 10043, 10044);
|
||||
|
||||
// 返回
|
||||
return list;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### 4.2、然后在 sso-client 端调用这个接口
|
||||
``` java
|
||||
// 查询我的账号信息
|
||||
@RequestMapping("/sso/myFollowList")
|
||||
public Object myFollowList() {
|
||||
// 组织url,加上签名参数
|
||||
String url = SaSsoUtil.addSignParams("http://sa-sso-server.com:9000/sso/getFollowList", StpUtil.getLoginId());
|
||||
|
||||
// 调用,并返回 SaResult 结果
|
||||
SaResult res = SaSsoUtil.request(url);
|
||||
|
||||
// 返回给前端
|
||||
return res;
|
||||
// 组织请求参数
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("apiType", "followList"); // 关键代码,代表本次我要拉取关注列表
|
||||
map.put("loginId", StpUtil.getLoginId());
|
||||
|
||||
// 发起请求
|
||||
Object resData = SaSsoUtil.getData(map);
|
||||
System.out.println("sso-server 返回的信息:" + resData);
|
||||
return resData;
|
||||
}
|
||||
```
|
||||
|
||||
然后在 server 端我们通过不同的 apiType 值,返回不同的信息即可。
|
||||
|
||||
|
||||
#### 4.2、方式二:直接在调用接口时传入一个自定义 path
|
||||
|
||||
我们可以 client 端,调用 `SaSsoUtil.getData` 方法时,传入一个自定义 path,例如:
|
||||
|
||||
``` java
|
||||
// 查询我的账号信息
|
||||
@RequestMapping("/sso/myFansList")
|
||||
public Object myFansList() {
|
||||
// 组织请求参数
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
// map.put("apiType", "userinfo"); // 此时已经不需要 apiType 参数了
|
||||
map.put("loginId", StpUtil.getLoginId());
|
||||
|
||||
// 发起请求 (传入自定义的 path 地址)
|
||||
Object resData = SaSsoUtil.getData("/sso/getFansList", map);
|
||||
System.out.println("sso-server 返回的信息:" + resData);
|
||||
return resData;
|
||||
}
|
||||
```
|
||||
|
||||
同时,我们需要在 server 端开放这个自定义的 `/sso/getFansList` 接口:
|
||||
|
||||
``` java
|
||||
// 获取指定用户的粉丝列表
|
||||
@RequestMapping("/sso/getFansList")
|
||||
public Object getFansList(Long loginId) {
|
||||
System.out.println("---------------- 获取 loginId=" + loginId + " 的粉丝列表 ----------------");
|
||||
|
||||
// 校验签名:只有拥有正确秘钥发起的请求才能通过校验
|
||||
SaSignUtil.checkRequest(SaHolder.getRequest());
|
||||
|
||||
// 查询数据 (此处仅做模拟)
|
||||
List<Integer> list = Arrays.asList(10041, 10042, 10043, 10044);
|
||||
|
||||
// 返回
|
||||
return list;
|
||||
}
|
||||
```
|
||||
|
||||
**注意:使用此方案时,需要在 client 端配置 `sa-token.sso.server-url` 地址,例如:**
|
||||
``` yaml
|
||||
sa-token:
|
||||
sso:
|
||||
# sso-server 端主机地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
```
|
||||
|
||||
#### 4.3、访问测试
|
||||
访问测试:[http://sa-sso-client1.com:9001/sso/myFollowList](http://sa-sso-client1.com:9001/sso/myFollowList)
|
||||
访问测试:[http://sa-sso-client1.com:9001/sso/myFansList](http://sa-sso-client1.com:9001/sso/myFansList)
|
||||
|
||||
|
||||
|
||||
@ -233,12 +284,11 @@ public Object myFollowList() {
|
||||
``` yaml
|
||||
sa-token:
|
||||
sso:
|
||||
# 打开单点注销功能
|
||||
is-slo: true
|
||||
# 单点注销地址
|
||||
slo-url: http://sa-sso-server.com:9000/sso/signout
|
||||
# 接口调用秘钥
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
sign:
|
||||
# API 接口调用秘钥
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
```
|
||||
<!------------- tab:properties 风格 ------------->
|
||||
``` properties
|
||||
@ -247,7 +297,7 @@ sa-token.sso.is-slo=true
|
||||
# 单点注销地址
|
||||
sa-token.sso.slo-url=http://sa-sso-server.com:9000/sso/signout
|
||||
# 接口调用秘钥
|
||||
sa-token.sso.secretkey=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
sa-token.sign.secret-key=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
```
|
||||
<!---------------------------- tabs:end ---------------------------->
|
||||
|
||||
|
@ -53,7 +53,7 @@ public class SaTokenConfigure {
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
|
@ -110,7 +110,7 @@ public class SaTokenConfigure {
|
||||
---
|
||||
### 所有可配置项
|
||||
|
||||
你不必立刻掌握整个表格,只需要在用到某个功能时再详细查阅它即可
|
||||
**你不必立刻掌握整个表格,只需要在用到某个功能时再详细查阅它即可**
|
||||
|
||||
| 参数名称 | 类型 | 默认值 | 说明 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
@ -120,6 +120,7 @@ public class SaTokenConfigure {
|
||||
| isConcurrent | Boolean | true | 是否允许同一账号并发登录 (为 true 时允许一起登录,为 false 时新登录挤掉旧登录) |
|
||||
| isShare | Boolean | true | 在多人登录同一账号时,是否共用一个token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token) |
|
||||
| maxLoginCount | int | 12 | 同一账号最大登录数量,-1代表不限 (只有在 `isConcurrent=true`, `isShare=false` 时此配置才有效),[详解](/use/config?id=配置项详解:maxlogincount) |
|
||||
| maxTryTimes | int | 12 | 在每次创建 Token 时的最高循环次数,用于保证 Token 唯一性(-1=不循环重试,直接使用) |
|
||||
| isReadBody | Boolean | true | 是否尝试从 请求体 里读取 Token |
|
||||
| isReadHeader | Boolean | true | 是否尝试从 header 里读取 Token |
|
||||
| isReadCookie | Boolean | true | 是否尝试从 cookie 里读取 Token,此值为 false 后,`StpUtil.login(id)` 登录时也不会再往前端注入Cookie |
|
||||
@ -148,6 +149,31 @@ Cookie相关配置:
|
||||
| httpOnly | Boolean | false | 是否禁止 js 操作 Cookie |
|
||||
| sameSite | String | Lax | 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制) |
|
||||
|
||||
Cookie 配置示例:
|
||||
|
||||
<!---------------------------- tabs:start ---------------------------->
|
||||
<!------------- tab:yaml 风格 ------------->
|
||||
``` yaml
|
||||
# Sa-Token 配置
|
||||
sa-token:
|
||||
# Cookie 相关配置
|
||||
cookie:
|
||||
domain: stp.com
|
||||
path: /
|
||||
secure: false
|
||||
httpOnly: true
|
||||
sameSite: Lax
|
||||
```
|
||||
<!------------- tab:properties 风格 ------------->
|
||||
``` properties
|
||||
# Cookie 相关配置
|
||||
sa-token.cookie.domain=stp.com
|
||||
sa-token.cookie.path=/
|
||||
sa-token.cookie.secure=false
|
||||
sa-token.cookie.httpOnly=true
|
||||
sa-token.cookie.sameSite=Lax
|
||||
```
|
||||
<!---------------------------- tabs:end ---------------------------->
|
||||
|
||||
|
||||
### 单点登录相关配置
|
||||
@ -319,7 +345,7 @@ sa-token.oauth2.is-client=true
|
||||
|
||||
#### 配置项详解:serverUrl
|
||||
|
||||
配置含义:配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、userinfoUrl、sloUrl 属性前面,用以简化各种 url 配置。
|
||||
配置含义:配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置。
|
||||
|
||||
在开发 SSO 模块时,我们需要在 sso-client 配置认证中心的各种地址,特别是在模式三下,一般代码会变成这样:
|
||||
|
||||
@ -332,8 +358,8 @@ sa-token:
|
||||
check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket
|
||||
# 单点注销地址
|
||||
slo-url: http://sa-sso-server.com:9000/sso/signout
|
||||
# SSO-Server端 查询userinfo地址
|
||||
userinfo-url: http://sa-sso-server.com:9000/sso/userinfo
|
||||
# SSO-Server端 查询数据地址
|
||||
get-data-url: http://sa-sso-server.com:9000/sso/getData
|
||||
```
|
||||
|
||||
一堆 xxx-url 配置比较繁琐,且含有大量重复字符,现在我们可以将其简化为:
|
||||
|
@ -4,6 +4,9 @@ import org.apache.dubbo.rpc.RpcContext;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request for Dubbo
|
||||
*
|
||||
@ -42,6 +45,24 @@ public class SaRequestForDubbo implements SaRequest {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -3,6 +3,9 @@ package cn.dev33.satoken.context.dubbo3.model;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request for Dubbo3
|
||||
*
|
||||
@ -41,6 +44,24 @@ public class SaRequestForDubbo3 implements SaRequest {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -3,6 +3,9 @@ package cn.dev33.satoken.context.grpc.model;
|
||||
import cn.dev33.satoken.context.grpc.context.SaTokenGrpcContext;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request for grpc
|
||||
*
|
||||
@ -27,6 +30,24 @@ public class SaRequestForGrpc implements SaRequest {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -212,12 +212,21 @@ public class StpLogicJwtForMixin extends StpLogic {
|
||||
// ------------------- Bean对象代理 -------------------
|
||||
|
||||
/**
|
||||
* 返回全局配置对象的isShare属性
|
||||
* 返回全局配置对象的 isShare 属性
|
||||
* @return /
|
||||
*/
|
||||
@Override
|
||||
public boolean getConfigOfIsShare() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回全局配置对象的 maxTryTimes 属性
|
||||
* @return /
|
||||
*/
|
||||
@Override
|
||||
public int getConfigOfMaxTryTimes() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,11 +43,6 @@ public class SaSsoConfig implements Serializable {
|
||||
*/
|
||||
public Boolean isHttp = false;
|
||||
|
||||
/**
|
||||
* 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
*/
|
||||
public String secretkey;
|
||||
|
||||
|
||||
// ----------------- Client端相关配置
|
||||
|
||||
@ -61,31 +56,36 @@ public class SaSsoConfig implements Serializable {
|
||||
*/
|
||||
public String authUrl = "/sso/auth";
|
||||
|
||||
/**
|
||||
* 是否打开单点注销功能
|
||||
*/
|
||||
// public Boolean isSlo = true; // 同Server端
|
||||
// /**
|
||||
// * 是否打开单点注销功能
|
||||
// */
|
||||
// public Boolean isSlo = true; // 同Server端,不再重复声明
|
||||
|
||||
/**
|
||||
* 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
// public Boolean isHttp = false; // 同Server端
|
||||
// /**
|
||||
// * 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
// */
|
||||
// public Boolean isHttp = false; // 同Server端,不再重复声明
|
||||
|
||||
/**
|
||||
* 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
*/
|
||||
// public String secretkey; // 同Server端
|
||||
// /**
|
||||
// * 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
// */
|
||||
// public String secretkey; // 同Server端,不再重复声明
|
||||
|
||||
/**
|
||||
* 配置 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String checkTicketUrl = "/sso/checkTicket";
|
||||
|
||||
/**
|
||||
* 配置 Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String getDataUrl = "/sso/getData";
|
||||
|
||||
/**
|
||||
* 配置 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String userinfoUrl = "/sso/userinfo";
|
||||
|
||||
|
||||
/**
|
||||
* 配置 Server 端单点注销地址
|
||||
*/
|
||||
@ -97,21 +97,10 @@ public class SaSsoConfig implements Serializable {
|
||||
public String ssoLogoutCall;
|
||||
|
||||
/**
|
||||
* 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、userinfoUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
* 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
*/
|
||||
public String serverUrl;
|
||||
|
||||
// ----------------- 其它
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距
|
||||
*/
|
||||
public long timestampDisparity = 1000 * 60 * 10;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return Ticket有效期 (单位: 秒)
|
||||
@ -177,22 +166,6 @@ public class SaSsoConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
*/
|
||||
public String getSecretkey() {
|
||||
return secretkey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param secretkey 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSecretkey(String secretkey) {
|
||||
this.secretkey = secretkey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
@ -240,6 +213,22 @@ public class SaSsoConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String getGetDataUrl() {
|
||||
return getDataUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param getDataUrl 配置 Server 端查询数据 getData 地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setGetDataUrl(String getDataUrl) {
|
||||
this.getDataUrl = getDataUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端查询 userinfo 地址
|
||||
*/
|
||||
@ -289,14 +278,14 @@ public class SaSsoConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、userinfoUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
* @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
*/
|
||||
public String getServerUrl() {
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、userinfoUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
* @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setServerUrl(String serverUrl) {
|
||||
@ -304,38 +293,21 @@ public class SaSsoConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距
|
||||
*/
|
||||
public long getTimestampDisparity() {
|
||||
return timestampDisparity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timestampDisparity 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setTimestampDisparity(long timestampDisparity) {
|
||||
this.timestampDisparity = timestampDisparity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSsoConfig ["
|
||||
+ "ticketTimeout=" + ticketTimeout
|
||||
+ ", allowUrl=" + allowUrl
|
||||
+ ", isSlo=" + isSlo
|
||||
+ ", isHttp=" + isHttp
|
||||
+ ", secretkey=" + secretkey
|
||||
+ ", isHttp=" + isHttp
|
||||
+ ", client=" + client
|
||||
+ ", authUrl=" + authUrl
|
||||
+ ", checkTicketUrl=" + checkTicketUrl
|
||||
+ ", getDataUrl=" + getDataUrl
|
||||
+ ", userinfoUrl=" + userinfoUrl
|
||||
+ ", sloUrl=" + sloUrl
|
||||
+ ", ssoLogoutCall=" + ssoLogoutCall
|
||||
+ ", serverUrl=" + serverUrl
|
||||
+ ", timestampDisparity=" + timestampDisparity
|
||||
+ ", serverUrl=" + serverUrl
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@ -356,6 +328,13 @@ public class SaSsoConfig implements Serializable {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getCheckTicketUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String splicingGetDataUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端查询 userinfo 地址
|
||||
*/
|
||||
|
@ -193,7 +193,7 @@ public class SaSsoProcessor {
|
||||
String loginId = req.getParam(paramName.loginId);
|
||||
|
||||
// step.1 校验签名
|
||||
ssoTemplate.checkSign(req);
|
||||
ssoTemplate.getSignTemplate().checkRequest(req);
|
||||
|
||||
// step.2 单点注销
|
||||
ssoTemplate.ssoLogout(loginId);
|
||||
@ -374,7 +374,7 @@ public class SaSsoProcessor {
|
||||
String loginId = req.getParamNotNull(paramName.loginId);
|
||||
|
||||
// 注销当前应用端会话
|
||||
ssoTemplate.checkSign(req);
|
||||
ssoTemplate.getSignTemplate().checkRequest(req);
|
||||
stpLogic.logout(loginId);
|
||||
|
||||
// 响应
|
||||
|
@ -1,16 +1,9 @@
|
||||
package cn.dev33.satoken.sso;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.sign.SaSignTemplate;
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.name.ApiName;
|
||||
@ -21,6 +14,8 @@ import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO 单点登录模块
|
||||
* @author kong
|
||||
@ -73,8 +68,16 @@ public class SaSsoTemplate {
|
||||
public SaSsoConfig getSsoConfig() {
|
||||
return SaSsoManager.getConfig();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取底层使用的 API 签名对象
|
||||
* @return /
|
||||
*/
|
||||
public SaSignTemplate getSignTemplate() {
|
||||
return SaManager.getSaSignTemplate();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- Ticket 操作 ----------------------
|
||||
|
||||
/**
|
||||
@ -153,7 +156,7 @@ public class SaSsoTemplate {
|
||||
}
|
||||
String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
|
||||
// 如果是 "a,b" 的格式,则只取最前面的一项
|
||||
if(loginId != null && loginId.indexOf(",") > -1) {
|
||||
if(loginId != null && loginId.contains(",")) {
|
||||
String[] arr = loginId.split(",");
|
||||
loginId = arr[0];
|
||||
}
|
||||
@ -206,7 +209,7 @@ public class SaSsoTemplate {
|
||||
|
||||
// 如果是 "a,b" 的格式,则解析出对应的 Client
|
||||
String ticketClient = null;
|
||||
if(loginId.indexOf(",") > -1) {
|
||||
if(loginId.contains(",")) {
|
||||
String[] arr = loginId.split(",");
|
||||
loginId = arr[0];
|
||||
ticketClient = arr[1];
|
||||
@ -252,7 +255,7 @@ public class SaSsoTemplate {
|
||||
public void checkRedirectUrl(String url) {
|
||||
|
||||
// 1、是否是一个有效的url
|
||||
if(SaFoxUtil.isUrl(url) == false) {
|
||||
if( ! SaFoxUtil.isUrl(url) ) {
|
||||
throw new SaSsoException("无效redirect:" + url).setCode(SaSsoErrorCode.CODE_30001);
|
||||
}
|
||||
|
||||
@ -264,12 +267,11 @@ public class SaSsoTemplate {
|
||||
|
||||
// 3、是否在[允许地址列表]之中
|
||||
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
||||
if(SaStrategy.me.hasElement.apply(authUrlList, url) == false) {
|
||||
if( ! SaStrategy.me.hasElement.apply(authUrlList, url) ) {
|
||||
throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002);
|
||||
}
|
||||
|
||||
// 校验通过 √
|
||||
return;
|
||||
// 校验通过 √
|
||||
}
|
||||
|
||||
|
||||
@ -285,7 +287,7 @@ public class SaSsoTemplate {
|
||||
return;
|
||||
}
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId);
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, ()-> new HashSet<String>());
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, HashSet::new);
|
||||
urlSet.add(sloCallbackUrl);
|
||||
session.set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet);
|
||||
}
|
||||
@ -304,9 +306,9 @@ public class SaSsoTemplate {
|
||||
|
||||
// step.1 遍历通知 Client 端注销会话
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, () -> new HashSet<String>());
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, HashSet::new);
|
||||
for (String url : urlSet) {
|
||||
url = addSignParams(url, loginId);
|
||||
url = joinLoginIdAndSign(url, loginId);
|
||||
cfg.getSendHttp().apply(url);
|
||||
}
|
||||
|
||||
@ -315,12 +317,23 @@ public class SaSsoTemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:账号资料
|
||||
* @param loginId 账号id
|
||||
* @return 账号资料
|
||||
* 根据配置的 getData 地址,查询数据
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public Object getUserinfo(Object loginId) {
|
||||
String url = buildUserinfoUrl(loginId);
|
||||
public Object getData(Map<String, Object> paramMap) {
|
||||
String getDataUrl = SaSsoManager.getConfig().splicingGetDataUrl();
|
||||
return getData(getDataUrl, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param path 自定义 path
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public Object getData(String path, Map<String, Object> paramMap) {
|
||||
String url = buildCustomPathUrl(path, paramMap);
|
||||
return SaSsoManager.getConfig().getSendHttp().apply(url);
|
||||
}
|
||||
|
||||
@ -355,17 +368,16 @@ public class SaSsoTemplate {
|
||||
* 部分 Servlet 版本 request.getRequestURL() 返回的 url 带有 query 参数,形如:http://domain.com?id=1,
|
||||
* 如果不加判断会造成最终生成的 serverAuthUrl 带有双 back 参数 ,这个 if 判断正是为了解决此问题
|
||||
*/
|
||||
if(clientLoginUrl.indexOf(paramName.back + "=" + back) == -1) {
|
||||
if( ! clientLoginUrl.contains(paramName.back + "=" + back) ) {
|
||||
clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back);
|
||||
}
|
||||
String serverAuthUrl = SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl);
|
||||
|
||||
|
||||
// 返回
|
||||
return serverAuthUrl;
|
||||
return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端向Client下放ticke的地址
|
||||
* 构建URL:Server端向Client下放ticket的地址
|
||||
* @param loginId 账号id
|
||||
* @param client 客户端标识
|
||||
* @param redirect Client端提供的重定向地址
|
||||
@ -412,16 +424,6 @@ public class SaSsoTemplate {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
* @return Server端 账号资料查询地址
|
||||
*/
|
||||
public String buildUserinfoUrl(Object loginId) {
|
||||
String userinfoUrl = SaSsoManager.getConfig().splicingUserinfoUrl();
|
||||
return addSignParams(userinfoUrl, loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:校验ticket的URL
|
||||
* <p> 在模式三下,Client端拿到Ticket后根据此地址向Server端发送请求,获取账号id
|
||||
@ -458,32 +460,39 @@ public class SaSsoTemplate {
|
||||
*/
|
||||
public String buildSloUrl(Object loginId) {
|
||||
String url = SaSsoManager.getConfig().splicingSloUrl();
|
||||
return addSignParams(url, loginId);
|
||||
return joinLoginIdAndSign(url, loginId);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 返回相应key -------------------
|
||||
|
||||
/**
|
||||
* 拼接key:Ticket 查 账号Id
|
||||
* @param ticket ticket值
|
||||
* @return key
|
||||
/**
|
||||
* 构建URL:Server端 getData 地址,带签名等参数
|
||||
* @param paramMap 查询参数
|
||||
* @return /
|
||||
*/
|
||||
public String splicingTicketSaveKey(String ticket) {
|
||||
return SaManager.getConfig().getTokenName() + ":ticket:" + ticket;
|
||||
public String buildGetDataUrl(Map<String, Object> paramMap) {
|
||||
String getDataUrl = SaSsoManager.getConfig().getGetDataUrl();
|
||||
return buildCustomPathUrl(getDataUrl, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:账号Id 反查 Ticket
|
||||
* @param id 账号id
|
||||
* @return key
|
||||
/**
|
||||
* 构建URL:Server 端自定义 path 地址,带签名等参数 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param paramMap 请求参数
|
||||
* @return /
|
||||
*/
|
||||
public String splicingTicketIndexKey(Object id) {
|
||||
return SaManager.getConfig().getTokenName() + ":id-ticket:" + id;
|
||||
public String buildCustomPathUrl(String path, Map<String, Object> paramMap) {
|
||||
// 如果path不是以 http 开头,那么就拼接上 serverUrl
|
||||
String url = path;
|
||||
if( ! url.startsWith("http") ) {
|
||||
String serverUrl = SaSsoManager.getConfig().getServerUrl();
|
||||
SaSsoException.throwByNull(serverUrl, "请先配置 sa-token.sso.server-url 地址", SaSsoErrorCode.CODE_30012);
|
||||
url = SaFoxUtil.spliceTwoUrl(serverUrl, path);
|
||||
}
|
||||
|
||||
// 添加签名等参数,并序列化
|
||||
return joinParamMapAndSign(url, paramMap);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 请求相关 -------------------
|
||||
// ------------------- 发起请求 -------------------
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
@ -497,98 +506,79 @@ public class SaSsoTemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:接口调用秘钥
|
||||
* @return see note
|
||||
* 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面
|
||||
* @param url 请求地址
|
||||
* @param paramMap 请求原始参数列表
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public String getSecretkey() {
|
||||
// 默认从配置文件中返回
|
||||
String secretkey = SaSsoManager.getConfig().getSecretkey();
|
||||
if(SaFoxUtil.isEmpty(secretkey)) {
|
||||
throw new SaSsoException("请配置 secretkey 参数").setCode(SaSsoErrorCode.CODE_30009);
|
||||
}
|
||||
return secretkey;
|
||||
public String joinParamMapAndSign(String url, Map<String, Object> paramMap) {
|
||||
// 在参数列表中追加:时间戳、随机字符串、参数签名
|
||||
SaManager.getSaSignTemplate().addSignParams(paramMap);
|
||||
|
||||
// 将参数列表序列化为kv字符串
|
||||
String signParams = SaManager.getSaSignTemplate().joinParams(paramMap);
|
||||
|
||||
// 将kv字符串拼接到url后面
|
||||
return SaFoxUtil.joinParam(url, signParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验secretkey秘钥是否有效 (API已过期,请更改为更安全的 sign 式校验)
|
||||
* @param secretkey 秘钥
|
||||
* 给 url 拼接 loginId 参数,并拼接 sign 等参数
|
||||
* @param url 链接
|
||||
* @param loginId 账号id
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public String joinLoginIdAndSign(String url, Object loginId) {
|
||||
Map<String, Object> paramMap = new LinkedHashMap<>();
|
||||
paramMap.put(paramName.loginId, loginId);
|
||||
return joinParamMapAndSign(url, paramMap);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 返回相应key -------------------
|
||||
|
||||
/**
|
||||
* 拼接key:Ticket 查 账号Id
|
||||
* @param ticket ticket值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketSaveKey(String ticket) {
|
||||
return SaManager.getConfig().getTokenName() + ":ticket:" + ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:账号Id 反查 Ticket
|
||||
* @param id 账号id
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketIndexKey(Object id) {
|
||||
return SaManager.getConfig().getTokenName() + ":id-ticket:" + id;
|
||||
}
|
||||
|
||||
|
||||
// -------- 以下方法已废弃,仅为兼容旧版本而保留 --------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
* @return Server端 账号资料查询地址
|
||||
*/
|
||||
@Deprecated
|
||||
public void checkSecretkey(String secretkey) {
|
||||
if(SaFoxUtil.isEmpty(secretkey) || secretkey.equals(getSecretkey()) == false) {
|
||||
throw new SaSsoException("无效秘钥:" + secretkey).setCode(SaSsoErrorCode.CODE_30003);
|
||||
}
|
||||
public String buildUserinfoUrl(Object loginId) {
|
||||
String userinfoUrl = SaSsoManager.getConfig().splicingUserinfoUrl();
|
||||
return joinLoginIdAndSign(userinfoUrl, loginId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据参数计算签名
|
||||
* 获取:账号资料
|
||||
* @param loginId 账号id
|
||||
* @param timestamp 当前时间戳,13位
|
||||
* @param nonce 随机字符串
|
||||
* @param secretkey 账号id
|
||||
* @return 签名
|
||||
* @return 账号资料
|
||||
*/
|
||||
public String getSign(Object loginId, String timestamp, String nonce, String secretkey) {
|
||||
Map<String, Object> map = new TreeMap<>();
|
||||
map.put(paramName.loginId, loginId);
|
||||
map.put(paramName.timestamp, timestamp);
|
||||
map.put(paramName.nonce, nonce);
|
||||
return SaManager.getSaSignTemplate().createSign(map, secretkey);
|
||||
@Deprecated
|
||||
public Object getUserinfo(Object loginId) {
|
||||
String url = buildUserinfoUrl(loginId);
|
||||
return request(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 url 追加 sign 等参数
|
||||
* @param url 连接
|
||||
* @param loginId 账号id
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public String addSignParams(String url, Object loginId) {
|
||||
|
||||
// 时间戳、随机字符串、参数签名
|
||||
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||
String nonce = SaFoxUtil.getRandomString(20);
|
||||
String sign = getSign(loginId, timestamp, nonce, getSecretkey());
|
||||
|
||||
// 追加到url
|
||||
url = SaFoxUtil.joinParam(url, paramName.loginId, loginId);
|
||||
url = SaFoxUtil.joinParam(url, paramName.timestamp, timestamp);
|
||||
url = SaFoxUtil.joinParam(url, paramName.nonce, nonce);
|
||||
url = SaFoxUtil.joinParam(url, paramName.sign, sign);
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验签名
|
||||
* @param req request
|
||||
*/
|
||||
public void checkSign(SaRequest req) {
|
||||
|
||||
// 参数签名、账号id、时间戳、随机字符串
|
||||
String sign = req.getParamNotNull(paramName.sign);
|
||||
String loginId = req.getParamNotNull(paramName.loginId);
|
||||
String timestamp = req.getParamNotNull(paramName.timestamp);
|
||||
String nonce = req.getParamNotNull(paramName.nonce);
|
||||
|
||||
// 校验时间戳
|
||||
checkTimestamp(Long.valueOf(timestamp));
|
||||
|
||||
// 校验签名
|
||||
String calcSign = getSign(loginId, timestamp, nonce, getSecretkey());
|
||||
if(calcSign.equals(sign) == false) {
|
||||
throw new SaSsoException("签名无效:" + calcSign).setCode(SaSsoErrorCode.CODE_30008);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验时间戳与当前时间的差距是否超出限制
|
||||
* @param timestamp 时间戳
|
||||
*/
|
||||
public void checkTimestamp(long timestamp) {
|
||||
long disparity = Math.abs(System.currentTimeMillis() - timestamp);
|
||||
long allowDisparity = SaSsoManager.getConfig().getTimestampDisparity();
|
||||
if(allowDisparity != -1 && disparity > allowDisparity) {
|
||||
throw new SaSsoException("timestamp 超出允许的范围").setCode(SaSsoErrorCode.CODE_30007);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package cn.dev33.satoken.sso;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO 单点登录模块 工具类
|
||||
* @author kong
|
||||
@ -139,12 +140,22 @@ public class SaSsoUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:账号资料
|
||||
* @param loginId 账号id
|
||||
* @return 账号资料
|
||||
* 获取:查询数据
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public static Object getUserinfo(Object loginId) {
|
||||
return ssoTemplate.getUserinfo(loginId);
|
||||
public static Object getData(Map<String, Object> paramMap) {
|
||||
return ssoTemplate.getData(paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义 path 查询数据 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param path 自定义 path
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public static Object getData(String path, Map<String, Object> paramMap) {
|
||||
return ssoTemplate.getData(path, paramMap);
|
||||
}
|
||||
|
||||
|
||||
@ -161,7 +172,7 @@ public class SaSsoUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端向Client下放ticke的地址
|
||||
* 构建URL:Server端向Client下放ticket的地址
|
||||
* @param loginId 账号id
|
||||
* @param client 客户端标识
|
||||
* @param redirect Client端提供的重定向地址
|
||||
@ -172,16 +183,25 @@ public class SaSsoUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
* @return Server端 账号资料查询地址
|
||||
* 构建URL:Server端 getData 地址,带签名等参数
|
||||
* @param paramMap 查询参数
|
||||
* @return /
|
||||
*/
|
||||
public static String buildUserinfoUrl(Object loginId) {
|
||||
return ssoTemplate.buildUserinfoUrl(loginId);
|
||||
public static String buildGetDataUrl(Map<String, Object> paramMap) {
|
||||
return ssoTemplate.buildGetDataUrl(paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server 端自定义 path 地址,带签名等参数 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param paramMap 请求参数
|
||||
* @return /
|
||||
*/
|
||||
public static String buildCustomPathUrl(String path, Map<String, Object> paramMap) {
|
||||
return ssoTemplate.buildCustomPathUrl(path, paramMap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------- 请求相关 -------------------
|
||||
// ------------------- 发起请求 -------------------
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
@ -193,50 +213,46 @@ public class SaSsoUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验secretkey秘钥是否有效 (API已过期,请更改为更安全的 sign 式校验)
|
||||
* @param secretkey 秘钥
|
||||
* 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面
|
||||
* @param url 请求地址
|
||||
* @param paramMap 请求原始参数列表
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public static String joinParamMapAndSign(String url, Map<String, Object> paramMap) {
|
||||
return ssoTemplate.joinLoginIdAndSign(url, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 url 拼接 loginId 参数,并拼接 sign 等参数
|
||||
* @param url 链接
|
||||
* @param loginId 账号id
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public static String joinLoginIdAndSign(String url, Object loginId) {
|
||||
return ssoTemplate.joinLoginIdAndSign(url, loginId);
|
||||
}
|
||||
|
||||
|
||||
// -------- 以下方法已废弃,仅为兼容旧版本而保留 --------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
* @return Server端 账号资料查询地址
|
||||
*/
|
||||
@Deprecated
|
||||
public static void checkSecretkey(String secretkey) {
|
||||
ssoTemplate.checkSecretkey(secretkey);
|
||||
public static String buildUserinfoUrl(Object loginId) {
|
||||
return ssoTemplate.buildUserinfoUrl(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数计算签名
|
||||
* 获取:账号资料
|
||||
* @param loginId 账号id
|
||||
* @param timestamp 当前时间戳,13位
|
||||
* @param nonce 随机字符串
|
||||
* @param secretkey 账号id
|
||||
* @return 签名
|
||||
* @return 账号资料
|
||||
*/
|
||||
public static String getSign(Object loginId, String timestamp, String nonce, String secretkey) {
|
||||
return ssoTemplate.getSign(loginId, timestamp, nonce, secretkey);
|
||||
@Deprecated
|
||||
public static Object getUserinfo(Object loginId) {
|
||||
return ssoTemplate.getUserinfo(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 url 追加 sign 等参数
|
||||
* @param url 连接
|
||||
* @param loginId 账号id
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public static String addSignParams(String url, Object loginId) {
|
||||
return ssoTemplate.addSignParams(url, loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验签名
|
||||
* @param req request
|
||||
*/
|
||||
public static void checkSign(SaRequest req) {
|
||||
ssoTemplate.checkSign(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验时间戳与当前时间的差距是否超出限制
|
||||
* @param timestamp 时间戳
|
||||
*/
|
||||
public static void checkTimestamp(long timestamp) {
|
||||
ssoTemplate.checkTimestamp(timestamp);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,5 +40,8 @@ public interface SaSsoErrorCode {
|
||||
|
||||
/** 该 ticket 不属于当前 client */
|
||||
public static final int CODE_30011 = 30011;
|
||||
|
||||
|
||||
/** 当前缺少配置 server-url 地址 */
|
||||
public static final int CODE_30012 = 30012;
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
@ -12,6 +10,9 @@ import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Request for Jakarta Servlet
|
||||
* @author kong
|
||||
@ -48,6 +49,36 @@ public class SaRequestForServlet implements SaRequest {
|
||||
return request.getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
Enumeration<String> parameterNames = request.getParameterNames();
|
||||
List<String> list = new ArrayList<>();
|
||||
while (parameterNames.hasMoreElements()) {
|
||||
list.add(parameterNames.nextElement());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
// 获取所有参数
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
Map<String, String> map = new LinkedHashMap<>(parameterMap.size());
|
||||
for (String key : parameterMap.keySet()) {
|
||||
String[] values = parameterMap.get(key);
|
||||
map.put(key, values[0]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -91,8 +91,8 @@ public class SaTokenPathFilter implements SaFilter {
|
||||
public void doFilter(Controller ctx, FilterChain chain) throws Throwable {
|
||||
try {
|
||||
// 执行全局过滤器
|
||||
beforeAuth.run(null);
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
beforeAuth.run(null);
|
||||
auth.run(null);
|
||||
});
|
||||
|
||||
|
@ -91,8 +91,8 @@ public class SaTokenPathFilter implements SaFilter {
|
||||
public void doFilter(Controller ctx, FilterChain chain) throws Throwable {
|
||||
try {
|
||||
// 执行全局过滤器
|
||||
beforeAuth.run(null);
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
beforeAuth.run(null);
|
||||
auth.run(null);
|
||||
});
|
||||
|
||||
|
@ -119,9 +119,9 @@ public class SaReactorFilter implements SaFilter, WebFilter {
|
||||
// 写入全局上下文 (同步)
|
||||
SaReactorSyncHolder.setContext(exchange);
|
||||
|
||||
// 执行全局过滤器
|
||||
// 执行全局过滤器
|
||||
beforeAuth.run(null);
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
beforeAuth.run(null);
|
||||
auth.run(null);
|
||||
});
|
||||
|
||||
|
@ -12,6 +12,8 @@ import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Request for Reactor
|
||||
* @author kong
|
||||
@ -48,6 +50,25 @@ public class SaRequestForReactor implements SaRequest {
|
||||
return request.getQueryParams().getFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
Set<String> names = request.getQueryParams().keySet();
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
return request.getQueryParams().toSingleValueMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -119,9 +119,9 @@ public class SaReactorFilter implements SaFilter, WebFilter {
|
||||
// 写入全局上下文 (同步)
|
||||
SaReactorSyncHolder.setContext(exchange);
|
||||
|
||||
// 执行全局过滤器
|
||||
// 执行全局过滤器
|
||||
beforeAuth.run(null);
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
beforeAuth.run(null);
|
||||
auth.run(null);
|
||||
});
|
||||
|
||||
|
@ -12,6 +12,11 @@ import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Request for Reactor
|
||||
* @author kong
|
||||
@ -48,6 +53,25 @@ public class SaRequestForReactor implements SaRequest {
|
||||
return request.getQueryParams().getFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
Set<String> names = request.getQueryParams().keySet();
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
return request.getQueryParams().toSingleValueMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
@ -49,6 +50,36 @@ public class SaRequestForServlet implements SaRequest {
|
||||
return request.getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数名称
|
||||
* @return 参数名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
Enumeration<String> parameterNames = request.getParameterNames();
|
||||
List<String> list = new ArrayList<>();
|
||||
while (parameterNames.hasMoreElements()) {
|
||||
list.add(parameterNames.nextElement());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
// 获取所有参数
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
Map<String, String> map = new LinkedHashMap<>(parameterMap.size());
|
||||
for (String key : parameterMap.keySet()) {
|
||||
String[] values = parameterMap.get(key);
|
||||
map.put(key, values[0]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
|
@ -123,12 +123,13 @@ public class SaTokenFilter implements SaFilter, Filter { //之所以改名,为
|
||||
Handler mainHandler = Solon.app().router().matchMain(ctx);
|
||||
Action action = (mainHandler instanceof Action ? (Action) mainHandler : null);
|
||||
|
||||
//1.执行前置处理(主要是一些跨域之类的)
|
||||
if(beforeAuth != null) {
|
||||
beforeAuth.run(mainHandler);
|
||||
}
|
||||
|
||||
//先路径过滤下(包括了静态文件)
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
//1.执行前置处理(主要是一些跨域之类的)
|
||||
if(beforeAuth != null) {
|
||||
beforeAuth.run(mainHandler);
|
||||
}
|
||||
//2.执行注解处理
|
||||
if(authAnno(action)) {
|
||||
//3.执行规则处理(如果没有被 @SaIgnore 忽略)
|
||||
|
@ -172,19 +172,19 @@ public class SaTokenInterceptor implements RouterInterceptor {
|
||||
try {
|
||||
Action action = (mainHandler instanceof Action ? (Action) mainHandler : null);
|
||||
|
||||
//先路径过滤下(包括了静态文件)
|
||||
//1.执行前置处理(主要是一些跨域之类的)
|
||||
if(beforeAuth != null) {
|
||||
beforeAuth.run(mainHandler);
|
||||
}
|
||||
|
||||
//先路径过滤下(不包括静态文件)
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
//1.执行前置处理(主要是一些跨域之类的)
|
||||
if(beforeAuth != null) {
|
||||
beforeAuth.run(mainHandler);
|
||||
}
|
||||
//2.执行注解处理
|
||||
if(authAnno(action)) {
|
||||
//3.执行规则处理(如果没有被 @SaIgnore 忽略)
|
||||
auth.run(mainHandler);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (StopMatchException e) {
|
||||
|
||||
} catch (SaTokenException e) {
|
||||
|
@ -5,6 +5,11 @@ import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author noear
|
||||
* @since 1.4
|
||||
@ -27,6 +32,21 @@ public class SaRequestForSolon implements SaRequest {
|
||||
return ctx.param(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getParamNames(){
|
||||
Set<String> names = ctx.paramMap().keySet();
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [请求体] 里提交的所有参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getParamMap(){
|
||||
return ctx.paramMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String s) {
|
||||
return ctx.header(s);
|
||||
|
@ -111,9 +111,9 @@ public class SaServletFilter implements SaFilter, Filter {
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
try {
|
||||
// 执行全局过滤器
|
||||
// 执行全局过滤器
|
||||
beforeAuth.run(null);
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
beforeAuth.run(null);
|
||||
auth.run(null);
|
||||
});
|
||||
|
||||
|
@ -110,9 +110,9 @@ public class SaServletFilter implements SaFilter, Filter {
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
try {
|
||||
// 执行全局过滤器
|
||||
// 执行全局过滤器
|
||||
beforeAuth.run(null);
|
||||
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
|
||||
beforeAuth.run(null);
|
||||
auth.run(null);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user