From ac09cced2b46349d75af41e0bcc02be9b26ee74a Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 3 May 2023 04:23:48 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9AAPI=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=AD=BE=E5=90=8D=E6=89=80=E6=9C=89=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=9D=87=E8=BF=81=E7=A7=BB=E8=87=B3=20core=20=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E6=A8=A1=E5=9D=97=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/dev33/satoken/SaManager.java | 9 +- .../cn/dev33/satoken/config/SaSignConfig.java | 111 +++++++ .../dev33/satoken/config/SaTokenConfig.java | 32 +- .../satoken/exception/SaSignException.java | 48 +++ .../cn/dev33/satoken/sign/SaSignTemplate.java | 279 ++++++++++++++---- .../sign/SaSignTemplateDefaultImpl.java | 11 - .../cn/dev33/satoken/sign/SaSignUtil.java | 159 ++++++++++ .../java/com/pj/sso/SsoServerController.java | 6 +- .../src/main/resources/application.yml | 14 +- .../java/com/pj/sso/SsoClientController.java | 16 +- .../src/main/resources/application.yml | 9 +- sa-token-doc/sso/sso-questions.md | 7 + sa-token-doc/sso/sso-server.md | 21 +- sa-token-doc/sso/sso-type2.md | 4 - sa-token-doc/sso/sso-type3.md | 15 +- .../cn/dev33/satoken/config/SaSsoConfig.java | 54 +--- .../cn/dev33/satoken/sso/SaSsoProcessor.java | 4 +- .../cn/dev33/satoken/sso/SaSsoTemplate.java | 123 +++----- .../java/cn/dev33/satoken/sso/SaSsoUtil.java | 40 +-- 19 files changed, 661 insertions(+), 301 deletions(-) create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/config/SaSignConfig.java create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/exception/SaSignException.java delete mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplateDefaultImpl.java create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignUtil.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java b/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java index ce6f24fd..8e0d108b 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java @@ -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(); } } } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSignConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSignConfig.java new file mode 100644 index 00000000..90ca38d2 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSignConfig.java @@ -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 + + "]"; + } + +} \ No newline at end of file diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java index e0ff46f9..5e82d7c4 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java @@ -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 *

@@ -109,7 +109,12 @@ public class SaTokenConfig implements Serializable { * Cookie配置对象 */ public SaCookieConfig cookie = new SaCookieConfig(); - + + /** + * API 签名配置对象 + */ + public SaSignConfig sign = new SaSignConfig(); + /** * @return token名称 (同时也是cookie名称) @@ -532,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 [" @@ -561,7 +582,8 @@ public class SaTokenConfig implements Serializable { + ", currDomain=" + currDomain + ", sameTokenTimeout=" + sameTokenTimeout + ", checkSameToken=" + checkSameToken - + ", cookie=" + cookie + + ", cookie=" + cookie + + ", sign=" + sign + "]"; } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/exception/SaSignException.java b/sa-token-core/src/main/java/cn/dev33/satoken/exception/SaSignException.java new file mode 100644 index 00000000..491bed46 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/exception/SaSignException.java @@ -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); + } + } + +} diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplate.java b/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplate.java index 346e8773..c9057058 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplate.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplate.java @@ -1,7 +1,10 @@ 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.SaTokenException; +import cn.dev33.satoken.exception.SaSignException; import cn.dev33.satoken.secure.SaSecureUtil; import cn.dev33.satoken.util.SaFoxUtil; @@ -9,19 +12,74 @@ import java.util.Map; import java.util.TreeMap; /** - * 参数签名算法 + * API 参数签名算法 * * @author kong * @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 paramsMap) { + public String joinParams(Map paramsMap) { // 按照 k1=v1&k2=v2&k3=v3 排列 StringBuilder sb = new StringBuilder(); @@ -46,7 +104,7 @@ public interface SaSignTemplate { * @param paramsMap 参数列表 * @return 拼接出的参数字符串 */ - public default String joinParamsDictSort(Map paramsMap) { + public String joinParamsDictSort(Map paramsMap) { // 保证字段按照字典顺序排列 if( ! (paramsMap instanceof TreeMap) ) { paramsMap = new TreeMap<>(paramsMap); @@ -56,62 +114,40 @@ public interface SaSignTemplate { return joinParams(paramsMap); } + + // ----------- 创建签名 + /** * 创建签名:md5(paramsStr + keyStr) - * @param paramsMap 参数列表 - * @param key 秘钥 + * @param paramsMap 参数列表 * @return 签名 */ - public default String createSign(Map paramsMap, String key) { - SaTokenException.throwByNull(key, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201); + public String createSign(Map paramsMap) { + String secretKey = getSecretKey(); + SaSignException.throwByNull(secretKey, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201); // 如果调用者不小心传入了 sign 参数,则此处需要将 sign 参数排除在外 - // 为了保证不影响原有的 paramsMap,此处需要再复制一份 - if(paramsMap.containsKey("sign")) { + if(paramsMap.containsKey(sign)) { + // 为了保证不影响原有的 paramsMap,此处需要再复制一份 paramsMap = new TreeMap<>(paramsMap); - paramsMap.remove("sign"); + 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 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 paramsMap, String key, String sign) { - if( ! isValidSign(paramsMap, key, sign) ) { - throw new SaTokenException("无效签名:" + sign).setCode(SaErrorCode.CODE_12202); - } - } - /** * 给 paramsMap 追加 timestamp、nonce、sign 三个参数 - * @param paramsMap 参数列表 - * @param key 秘钥 + * @param paramsMap 参数列表 * @return 加工后的参数列表 */ - public default Map addSignParams(Map paramsMap, String key) { - paramsMap.put("timestamp", String.valueOf(System.currentTimeMillis())); - paramsMap.put("nonce", SaFoxUtil.getRandomString(32)); - paramsMap.put("sign", createSign(paramsMap, key)); + public Map addSignParams(Map paramsMap) { + paramsMap.put(timestamp, String.valueOf(System.currentTimeMillis())); + paramsMap.put(nonce, SaFoxUtil.getRandomString(32)); + paramsMap.put(sign, createSign(paramsMap)); return paramsMap; } @@ -119,24 +155,26 @@ public interface SaSignTemplate { * 给 paramsMap 追加 timestamp、nonce、sign 三个参数,并转换为参数字符串,形如: * data=xxx8nonce=xxx8timestamp=xxx8sign=xxx * @param paramsMap 参数列表 - * @param key 秘钥 * @return 加工后的参数列表 转化为的参数字符串 */ - public default String addSignParamsAndToString(Map paramsMap, String key) { + public String addSignParamsAndJoin(Map paramsMap) { // 追加参数 - paramsMap = addSignParams(paramsMap, key); + 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; } @@ -144,29 +182,142 @@ public interface SaSignTemplate { /** * 校验:指定时间戳与当前时间戳的差距是否在允许的范围内,如果超出则抛出异常 * @param timestamp 待校验的时间戳 - * @param allowDisparity 允许的最大时间差(单位:ms),-1 代表不限制 */ - public default void checkTimestamp(long timestamp, long allowDisparity) { - if( ! isValidTimestamp(timestamp, allowDisparity) ) { - 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; + } /** - * 请更换为 addSignParamsAndToString - * @param paramsMap 参数列表 - * @param key 秘钥 - * @return 加工后的参数列表 转化为的参数字符串 + * 校验:随机字符串 nonce 是否有效,如果无效则抛出异常。 + * 注意:同一 nonce 只可以被校验通过一次,校验后将保存在缓存中,再次校验将无法通过 + * @param nonce 待校验的随机字符串 */ - @Deprecated - public default String addSignParamsToString(Map paramsMap, String key) { - // 追加参数 - paramsMap = addSignParams(paramsMap, key); + public void checkNonce(String nonce) { + // 为空代表无效 + if(SaFoxUtil.isEmpty(nonce)) { + throw new SaSignException("nonce 为空,无效"); + } - // . - return joinParams(paramsMap); + // 校验此 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 paramsMap, String sign) { + String theSign = createSign(paramsMap); + return theSign.equals(sign); + } + + /** + * 校验:给定的参数 + 秘钥 生成的签名是否为有效签名,如果签名无效则抛出异常 + * @param paramsMap 参数列表 + * @param sign 待验证的签名 + */ + public void checkSign(Map 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 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 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; } } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplateDefaultImpl.java b/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplateDefaultImpl.java deleted file mode 100644 index f8137033..00000000 --- a/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignTemplateDefaultImpl.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.dev33.satoken.sign; - -/** - * 参数签名算法 [默认实现类] - * - * @author kong - * @since: 2022-4-27 - */ -public class SaSignTemplateDefaultImpl implements SaSignTemplate { - -} diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignUtil.java new file mode 100644 index 00000000..649b9b2c --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/sign/SaSignUtil.java @@ -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 paramsMap) { + return SaManager.getSaSignTemplate().joinParams(paramsMap); + } + + /** + * 将所有参数按照字典顺序连接成一个字符串,形如:a=18b=28c=3 + * @param paramsMap 参数列表 + * @return 拼接出的参数字符串 + */ + public static String joinParamsDictSort(Map paramsMap) { + return SaManager.getSaSignTemplate().joinParamsDictSort(paramsMap); + } + + + // ----------- 创建签名 + + /** + * 创建签名:md5(paramsStr + keyStr) + * @param paramsMap 参数列表 + * @return 签名 + */ + public static String createSign(Map paramsMap) { + return SaManager.getSaSignTemplate().createSign(paramsMap); + } + + /** + * 给 paramsMap 追加 timestamp、nonce、sign 三个参数 + * @param paramsMap 参数列表 + * @return 加工后的参数列表 + */ + public static Map addSignParams(Map paramsMap) { + return SaManager.getSaSignTemplate().addSignParams(paramsMap); + } + + /** + * 给 paramsMap 追加 timestamp、nonce、sign 三个参数,并转换为参数字符串,形如: + * data=xxx8nonce=xxx8timestamp=xxx8sign=xxx + * @param paramsMap 参数列表 + * @return 加工后的参数列表 转化为的参数字符串 + */ + public static String addSignParamsAndJoin(Map 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 paramsMap, String sign) { + return SaManager.getSaSignTemplate().isValidSign(paramsMap, sign); + } + + /** + * 校验:给定的参数 + 秘钥 生成的签名是否为有效签名,如果签名无效则抛出异常 + * @param paramsMap 参数列表 + * @param sign 待验证的签名 + */ + public static void checkSign(Map paramsMap, String sign) { + SaManager.getSaSignTemplate().checkSign(paramsMap, sign); + } + + /** + * 判断:参数列表中的 nonce、timestamp、sign 是否均为合法的 + * @param paramMap 待校验的请求参数集合 + * @return 是否合法 + */ + public static boolean isValidParamMap(Map paramMap) { + return SaManager.getSaSignTemplate().isValidParamMap(paramMap); + } + + /** + * 校验:参数列表中的 nonce、timestamp、sign 是否均为合法的,如果不合法,则抛出对应的异常 + * @param paramMap 待校验的请求参数集合 + */ + public static void checkParamMap(Map 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); + } + +} diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java index 8c72a99f..d399e905 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java @@ -2,8 +2,8 @@ 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.sso.SaSsoUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import com.dtflys.forest.Forest; @@ -66,13 +66,13 @@ public class SsoServerController { // 示例:获取数据接口(用于在模式三下,为 client 端开放拉取数据的接口) @RequestMapping("/sso/getData") - public Object userinfo(String apiType, String loginId) { + public Object getData(String apiType, String loginId) { System.out.println("---------------- 获取数据 ----------------"); System.out.println("apiType=" + apiType); System.out.println("loginId=" + loginId); // 校验签名:只有拥有正确秘钥发起的请求才能通过校验 - SaSsoUtil.checkSign(SaHolder.getRequest()); + SaSignUtil.checkRequest(SaHolder.getRequest()); // 自定义返回结果(模拟) return SaResult.ok() diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml index be264665..c7937759 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml @@ -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: diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java index cf1d77a0..adbcbeef 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java @@ -1,21 +1,17 @@ package com.pj.sso; -import cn.dev33.satoken.context.SaHolder; -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.List; import java.util.Map; /** @@ -65,7 +61,7 @@ public class SsoClientController { map.put("loginId", StpUtil.getLoginId()); // 发起请求 - Object resData = SaSsoUtil.getData("/sso/getData", map); + Object resData = SaSsoUtil.getData(map); System.out.println("sso-server 返回的信息:" + resData); return resData; } diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml index 67632540..a61bc655 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml @@ -8,18 +8,17 @@ 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 # 查询数据地址 get-data-url: http://sa-sso-server.com:9000/sso/getData + sign: + # API 接口调用秘钥 + secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor spring: # 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis) diff --git a/sa-token-doc/sso/sso-questions.md b/sa-token-doc/sso/sso-questions.md index 1d0b8843..2be82544 100644 --- a/sa-token-doc/sso/sso-questions.md +++ b/sa-token-doc/sso/sso-questions.md @@ -184,6 +184,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"); diff --git a/sa-token-doc/sso/sso-server.md b/sa-token-doc/sso/sso-server.md index 6b1288d1..06fd6a87 100644 --- a/sa-token-doc/sso/sso-server.md +++ b/sa-token-doc/sso/sso-server.md @@ -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请求处理器(文档有步骤说明) diff --git a/sa-token-doc/sso/sso-type2.md b/sa-token-doc/sso/sso-type2.md index a38f3153..901cc9e1 100644 --- a/sa-token-doc/sso/sso-type2.md +++ b/sa-token-doc/sso/sso-type2.md @@ -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数据库索引 diff --git a/sa-token-doc/sso/sso-type3.md b/sa-token-doc/sso/sso-type3.md index ea504b69..194d6a4d 100644 --- a/sa-token-doc/sso/sso-type3.md +++ b/sa-token-doc/sso/sso-type3.md @@ -115,13 +115,13 @@ forest.log-enabled: false ``` java // 示例:获取数据接口(用于在模式三下,为 client 端开放拉取数据的接口) @RequestMapping("/sso/getData") -public Object userinfo(String apiType, String loginId) { +public Object getData(String apiType, String loginId) { System.out.println("---------------- 获取数据 ----------------"); System.out.println("apiType=" + apiType); System.out.println("loginId=" + loginId); // 校验签名:只有拥有正确秘钥发起的请求才能通过校验 - SaSsoUtil.checkSign(SaHolder.getRequest()); + SaSignUtil.checkRequest(SaHolder.getRequest()); // 自定义返回结果(模拟) return SaResult.ok() @@ -232,7 +232,7 @@ public Object getFansList(Long loginId) { System.out.println("---------------- 获取 loginId=" + loginId + " 的粉丝列表 ----------------"); // 校验签名:只有拥有正确秘钥发起的请求才能通过校验 - SaSsoUtil.checkSign(SaHolder.getRequest()); + SaSignUtil.checkRequest(SaHolder.getRequest()); // 查询数据 (此处仅做模拟) List list = Arrays.asList(10041, 10042, 10043, 10044); @@ -284,12 +284,11 @@ sa-token: ``` yaml sa-token: sso: - # 打开单点注销功能 - is-slo: true # 单点注销地址 slo-url: http://sa-sso-server.com:9000/sso/signout - # 接口调用秘钥 - secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + sign: + # API 接口调用秘钥 + secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor ``` ``` properties @@ -298,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 ``` diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java index 40e536d7..8909fcd8 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java @@ -43,11 +43,6 @@ public class SaSsoConfig implements Serializable { */ public Boolean isHttp = false; - /** - * 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验) - */ - public String secretkey; - // ----------------- Client端相关配置 @@ -106,17 +101,6 @@ public class SaSsoConfig implements Serializable { */ public String serverUrl; - // ----------------- 其它 - - - - /** - * 接口调用时的时间戳允许的差距(单位:ms),-1代表不校验差距 - */ - public long timestampDisparity = 1000 * 60 * 10; - - - /** * @return Ticket有效期 (单位: 秒) @@ -182,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 码的互相锁定 */ @@ -325,30 +293,13 @@ 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 @@ -356,8 +307,7 @@ public class SaSsoConfig implements Serializable { + ", userinfoUrl=" + userinfoUrl + ", sloUrl=" + sloUrl + ", ssoLogoutCall=" + ssoLogoutCall - + ", serverUrl=" + serverUrl - + ", timestampDisparity=" + timestampDisparity + + ", serverUrl=" + serverUrl + "]"; } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java index bcb1d227..b75702bd 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java @@ -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); // 响应 diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java index 128427f6..4386090a 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java @@ -2,8 +2,8 @@ package cn.dev33.satoken.sso; 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; @@ -68,8 +68,16 @@ public class SaSsoTemplate { public SaSsoConfig getSsoConfig() { return SaSsoManager.getConfig(); } - - + + /** + * 获取底层使用的 API 签名对象 + * @return / + */ + public SaSignTemplate getSignTemplate() { + return SaManager.getSaSignTemplate(); + } + + // ---------------------- Ticket 操作 ---------------------- /** @@ -300,7 +308,7 @@ public class SaSsoTemplate { SaSsoConfig cfg = SaSsoManager.getConfig(); Set 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); } @@ -452,7 +460,7 @@ public class SaSsoTemplate { */ public String buildSloUrl(Object loginId) { String url = SaSsoManager.getConfig().splicingSloUrl(); - return addSignParams(url, loginId); + return joinLoginIdAndSign(url, loginId); } /** @@ -480,32 +488,11 @@ public class SaSsoTemplate { } // 添加签名等参数,并序列化 - return addSignParams(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; + return joinParamMapAndSign(url, paramMap); } - // ------------------- 请求相关 ------------------- + // ------------------- 发起请求 ------------------- /** * 发出请求,并返回 SaResult 结果 @@ -518,33 +505,20 @@ public class SaSsoTemplate { return new SaResult(map); } - /** - * 获取:接口调用秘钥 - * @return see note - */ - public String getSecretkey() { - // 默认从配置文件中返回 - String secretkey = SaSsoManager.getConfig().getSecretkey(); - if(SaFoxUtil.isEmpty(secretkey)) { - throw new SaSsoException("请配置 secretkey 参数").setCode(SaSsoErrorCode.CODE_30009); - } - return secretkey; - } - /** * 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面 * @param url 请求地址 * @param paramMap 请求原始参数列表 * @return 加工后的url */ - public String addSignParams(String url, Map paramMap) { - // 追加:时间戳、随机字符串、参数签名 - SaManager.getSaSignTemplate().addSignParams(paramMap, getSecretkey()); + public String joinParamMapAndSign(String url, Map paramMap) { + // 在参数列表中追加:时间戳、随机字符串、参数签名 + SaManager.getSaSignTemplate().addSignParams(paramMap); - // 序列化为kv字符串 + // 将参数列表序列化为kv字符串 String signParams = SaManager.getSaSignTemplate().joinParams(paramMap); - // 拼接到一起 + // 将kv字符串拼接到url后面 return SaFoxUtil.joinParam(url, signParams); } @@ -554,51 +528,36 @@ public class SaSsoTemplate { * @param loginId 账号id * @return 加工后的url */ - public String addSignParams(String url, Object loginId) { + public String joinLoginIdAndSign(String url, Object loginId) { Map paramMap = new LinkedHashMap<>(); paramMap.put(paramName.loginId, loginId); - return addSignParams(url, paramMap); + return joinParamMapAndSign(url, paramMap); + } + + + // ------------------- 返回相应key ------------------- + + /** + * 拼接key:Ticket 查 账号Id + * @param ticket ticket值 + * @return key + */ + public String splicingTicketSaveKey(String ticket) { + return SaManager.getConfig().getTokenName() + ":ticket:" + ticket; } /** - * 校验签名 - * @param req request + * 拼接key:账号Id 反查 Ticket + * @param id 账号id + * @return key */ - public void checkSign(SaRequest req) { - // 获取签名、时间戳、随机字符串 - String sign = req.getParamNotNull(paramName.sign); - String timestamp = req.getParamNotNull(paramName.timestamp); - String nonce = req.getParamNotNull(paramName.nonce); - - // 1、校验时间戳 - SaManager.getSaSignTemplate().checkTimestamp(Long.parseLong(timestamp), SaSsoManager.getConfig().getTimestampDisparity()); - - // 2、校验随机字符串 - - // 3、校验签名 - SaManager.getSaSignTemplate().checkSign(req.getParamMap(), getSecretkey(), sign); + public String splicingTicketIndexKey(Object id) { + return SaManager.getConfig().getTokenName() + ":id-ticket:" + id; } // -------- 以下方法已废弃,仅为兼容旧版本而保留 -------- - /** - * 根据参数计算签名 - * @param loginId 账号id - * @param timestamp 当前时间戳,13位 - * @param nonce 随机字符串 - * @param secretkey 账号id - * @return 签名 - */ - @Deprecated - public String getSign(Object loginId, String timestamp, String nonce, String secretkey) { - Map map = new TreeMap<>(); - map.put(paramName.loginId, loginId); - map.put(paramName.timestamp, timestamp); - map.put(paramName.nonce, nonce); - return SaManager.getSaSignTemplate().createSign(map, secretkey); - } - /** * 构建URL:Server端 账号资料查询地址 * @param loginId 账号id @@ -607,7 +566,7 @@ public class SaSsoTemplate { @Deprecated public String buildUserinfoUrl(Object loginId) { String userinfoUrl = SaSsoManager.getConfig().splicingUserinfoUrl(); - return addSignParams(userinfoUrl, loginId); + return joinLoginIdAndSign(userinfoUrl, loginId); } /** @@ -618,7 +577,7 @@ public class SaSsoTemplate { @Deprecated public Object getUserinfo(Object loginId) { String url = buildUserinfoUrl(loginId); - return SaSsoManager.getConfig().getSendHttp().apply(url); + return request(url); } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java index 4388c043..a4fc2880 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java @@ -1,6 +1,5 @@ package cn.dev33.satoken.sso; -import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.util.SaResult; import java.util.Map; @@ -202,7 +201,7 @@ public class SaSsoUtil { } - // ------------------- 请求相关 ------------------- + // ------------------- 发起请求 ------------------- /** * 发出请求,并返回 SaResult 结果 @@ -219,8 +218,8 @@ public class SaSsoUtil { * @param paramMap 请求原始参数列表 * @return 加工后的url */ - public static String addSignParams(String url, Map paramMap) { - return ssoTemplate.addSignParams(url, paramMap); + public static String joinParamMapAndSign(String url, Map paramMap) { + return ssoTemplate.joinLoginIdAndSign(url, paramMap); } /** @@ -229,32 +228,21 @@ public class SaSsoUtil { * @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); + public static String joinLoginIdAndSign(String url, Object loginId) { + return ssoTemplate.joinLoginIdAndSign(url, loginId); } // -------- 以下方法已废弃,仅为兼容旧版本而保留 -------- /** - * 根据参数计算签名 + * 构建URL:Server端 账号资料查询地址 * @param loginId 账号id - * @param timestamp 当前时间戳,13位 - * @param nonce 随机字符串 - * @param secretkey 账号id - * @return 签名 + * @return Server端 账号资料查询地址 */ @Deprecated - public static String getSign(Object loginId, String timestamp, String nonce, String secretkey) { - return ssoTemplate.getSign(loginId, timestamp, nonce, secretkey); + public static String buildUserinfoUrl(Object loginId) { + return ssoTemplate.buildUserinfoUrl(loginId); } /** @@ -267,14 +255,4 @@ public class SaSsoUtil { return ssoTemplate.getUserinfo(loginId); } - /** - * 构建URL:Server端 账号资料查询地址 - * @param loginId 账号id - * @return Server端 账号资料查询地址 - */ - @Deprecated - public static String buildUserinfoUrl(Object loginId) { - return ssoTemplate.buildUserinfoUrl(loginId); - } - }