将SSO模式三的接口调用改为签名式校验

This commit is contained in:
click33
2022-04-29 03:23:46 +08:00
parent 5e3795e29e
commit de39d91b71
23 changed files with 575 additions and 281 deletions

View File

@@ -15,6 +15,8 @@ import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl; import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.listener.SaTokenListenerDefaultImpl; import cn.dev33.satoken.listener.SaTokenListenerDefaultImpl;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.sign.SaSignTemplateDefaultImpl;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl; import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
@@ -191,6 +193,24 @@ public class SaManager {
} }
return saJsonTemplate; return saJsonTemplate;
} }
/**
* 参数签名 Bean
*/
private volatile static SaSignTemplate saSignTemplate;
public static void setSaSignTemplate(SaSignTemplate saSignTemplate) {
SaManager.saSignTemplate = saSignTemplate;
}
public static SaSignTemplate getSaSignTemplate() {
if (saSignTemplate == null) {
synchronized (SaManager.class) {
if (saSignTemplate == null) {
setSaSignTemplate(new SaSignTemplateDefaultImpl());
}
}
}
return saSignTemplate;
}
/** /**
* StpLogic集合, 记录框架所有成功初始化的StpLogic * StpLogic集合, 记录框架所有成功初始化的StpLogic

View File

@@ -0,0 +1,59 @@
package cn.dev33.satoken.sign;
import java.util.Map;
import java.util.TreeMap;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* 参数签名算法
*
* @author kong
* @date: 2022-4-27
*/
public interface SaSignTemplate {
/**
* 将所有参数连接成一个字符串,形如: k1=v1&k2=v2&k3=v3
* @param paramsMap 参数列表
* @return 字符串
*/
public default String joinParams(Map<String, Object> paramsMap) {
// 保证字段按照字典顺序排列
if(paramsMap instanceof TreeMap == false) {
paramsMap = new TreeMap<>(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) {
sb.append(key).append("=").append(value).append("&");
}
}
// 删除最后一位 &
if(sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
// .
return sb.toString();
}
/**
* 创建签名md5(paramsStr + keyStr)
* @param paramsMap 参数列表
* @param key 秘钥
* @return
*/
public default String createSign(Map<String, Object> paramsMap, String key) {
String paramsStr = joinParams(paramsMap);
String fullStr = paramsStr + "&key=" + key;
return SaSecureUtil.md5(fullStr);
}
}

View File

@@ -0,0 +1,11 @@
package cn.dev33.satoken.sign;
/**
* 参数签名算法 [默认实现类]
*
* @author kong
* @date: 2022-4-27
*/
public class SaSignTemplateDefaultImpl implements SaSignTemplate {
}

View File

@@ -43,6 +43,8 @@
if(res.code == 200) { if(res.code == 200) {
localStorage.setItem('satoken', res.data); localStorage.setItem('satoken', res.data);
location.href = decodeURIComponent(back); location.href = decodeURIComponent(back);
} else {
alert(res.msg);
} }
}) })
} }

View File

@@ -53,8 +53,15 @@ public class SsoServerController {
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉) // 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
sso.setSendHttp(url -> { sso.setSendHttp(url -> {
return OkHttps.sync(url).get().getBody().toString(); try {
// 发起 http 请求
System.out.println("发起请求:" + url);
return OkHttps.sync(url).get().getBody().toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}); });
} }
} }

View File

@@ -46,6 +46,7 @@ public class SsoClientController {
private void configSso(SaSsoConfig sso) { private void configSso(SaSsoConfig sso) {
// 配置Http请求处理器 // 配置Http请求处理器
sso.setSendHttp(url -> { sso.setSendHttp(url -> {
System.out.println("发起请求:" + url);
return OkHttps.sync(url).get().getBody().toString(); return OkHttps.sync(url).get().getBody().toString();
}); });
} }

View File

@@ -83,22 +83,33 @@ http://{host}:{port}/sso/checkTicket
http://{host}:{port}/sso/logout http://{host}:{port}/sso/logout
``` ```
接受参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| loginId | 否 | 要注销的账号id |
| secretkey | 否 | 接口通信秘钥 |
| back | 否 | 注销成功后的重定向地址 |
此接口有两种调用方式 此接口有两种调用方式
##### 方式一:在 Client 的前端页面引导用户直接跳转,并带有 back 参数 ##### 4.1、方式一:在 Client 的前端页面引导用户直接跳转,并带有 back 参数
例如:`http://{host}:{port}/sso/logout?back=xxx`代表用户注销成功后返回back地址 例如:
##### 方式二:在 Client 的后端通过 http 工具来调用 ``` url
例如:`http://{host}:{port}/sso/logout?loginId={value}&secretkey={value}`,代表注销 账号=loginId 的账号返回json数据结果形如 http://{host}:{port}/sso/logout?back=xxx
```
用户注销成功后将返回 back 地址
##### 4.2、方式二:在 Client 的后端通过 http 工具来调用
接受参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| loginId | 是 | 要注销的账号 id |
| timestamp | 是 | 当前时间戳13位 |
| nonce | 是 | 随机字符串 |
| sign | 是 | 签名,生成算法:`md5( loginId={value}&nonce={value}&timestamp={value}&key={secretkey秘钥} )` |
例如:
``` url
http://{host}:{port}/sso/logout?loginId={value}&timestamp={value}&nonce={value}&sign={value}
```
将返回 json 数据结果,形如:
``` js ``` js
{ {
@@ -113,7 +124,7 @@ http://{host}:{port}/sso/logout
``` js ``` js
{ {
"code": 500, // 200表示请求成功非200标识请求失败 "code": 500, // 200表示请求成功非200标识请求失败
"msg": "无效秘钥xxx", // 失败原因 "msg": "签名无效xxx", // 失败原因
"data": null "data": null
} }
``` ```

View File

@@ -5,9 +5,11 @@
### 问在模式一与模式二中Client端 必须通过 Alone-Redis 插件来访问Redis吗 ### 问在模式一与模式二中Client端 必须通过 Alone-Redis 插件来访问Redis吗
答:不必须,只是推荐,权限缓存与业务缓存分离后会减少 `SSO-Redis` 的访问压力,且可以避免多个 `Client端` 的缓存读写冲突 答:不必须,只是推荐,权限缓存与业务缓存分离后会减少 `SSO-Redis` 的访问压力,且可以避免多个 `Client端` 的缓存读写冲突
### 问:将旧有系统改造为单点登录时,应该注意哪些? ### 问:将旧有系统改造为单点登录时,应该注意哪些?
建议不要把其中一个系统改造为SSO服务端而是新起一个项目作为Server端所有旧有项目全部作为Client端与此对接 建议不要把其中一个系统改造为SSO服务端而是新起一个项目作为Server端所有旧有项目全部作为Client端与此对接
### 问SSO模式二第一个域名登录成功之后其他两个不会自动登录 ### 问SSO模式二第一个域名登录成功之后其他两个不会自动登录
系统1登录成功之后系统二与系统三需要点击登录按钮才会登录成功 系统1登录成功之后系统二与系统三需要点击登录按钮才会登录成功
@@ -15,13 +17,23 @@
> 第二个系统,需要:点击 [登录] 按钮 -> 登录成功 <br> > 第二个系统,需要:点击 [登录] 按钮 -> 登录成功 <br>
> 第三个系统,需要:点击 [登录] 按钮 -> 登录成功 (免去重复跳转登录页输入账号密码的步骤) > 第三个系统,需要:点击 [登录] 按钮 -> 登录成功 (免去重复跳转登录页输入账号密码的步骤)
### 追问:那我是否可以设计成不需要点登录按钮的,只要访问页面,它就能登录成功 ### 追问:那我是否可以设计成不需要点登录按钮的,只要访问页面,它就能登录成功
可以:加个过滤器检测到未登录 自动跳转就行了,详细可以参照章节:[[何时引导用户去登录]](/sso/sso-custom-login) 给出的建议进行设计 可以:加个过滤器检测到未登录 自动跳转就行了,详细可以参照章节:[[何时引导用户去登录]](/sso/sso-custom-login) 给出的建议进行设计
### 问我参照文档的SSO模式二搭建一直提示Ticket无效请问怎么回事 ### 问我参照文档的SSO模式二搭建一直提示Ticket无效请问怎么回事
根据群友的反馈,出现此异常概率最大的原因是因为 `Client``Server` 没有连接同一个RedisSSO模式二中两者必须连接同一个 Redis 才可以登录成功, 根据群友的反馈,出现此异常概率最大的原因是因为 `Client``Server` 没有连接同一个RedisSSO模式二中两者必须连接同一个 Redis 才可以登录成功,
如果您排查之后不是此原因可以加入QQ群或者在issues反馈一下 如果您排查之后不是此原因可以加入QQ群或者在issues反馈一下
### 模式一或者模式二报错Could not write JSON: No serializer found for class com.pj.sso.SysUser and no properties discovered to create BeanSerializer
一般是因为在 sso-server 端往 session 上写入了某个实体类(比如 User而在 sso-client 端没有这个实体类,导致反序列化失败。
解决方案:在 sso-client 也新建上这个类,而且包名需要与 sso-server 端的一致(直接从 sso-server 把实体类复制过来就好了)
### 还有其它问题? ### 还有其它问题?
可以加群反馈一下,比较典型的问题我们解决之后都会提交到此页面方便大家快速排查 可以加群反馈一下,比较典型的问题我们解决之后都会提交到此页面方便大家快速排查

View File

@@ -93,14 +93,23 @@ public class SsoServerController {
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉) // 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
cfg.sso.setSendHttp(url -> { cfg.sso.setSendHttp(url -> {
return OkHttps.sync(url).get().getBody().toString(); try {
// 发起 http 请求
System.out.println("发起请求:" + url);
return OkHttps.sync(url).get().getBody().toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}); });
} }
} }
``` ```
注:在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数可通过`SaHolder.getRequest().getParam("xxx")`来获取
- 在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数可通过`SaHolder.getRequest().getParam("xxx")`来获取
- 在 `setSendHttp` 函数中,使用 `try-catch` 是为了提高整个注销流程的容错性,避免在一些极端情况下注销失败(例如:某个 Client 端上线之后又下线,导致 http 请求无法调用成功,从而阻断了整个注销流程)
全局异常处理: 全局异常处理:
``` java ``` java

View File

@@ -41,6 +41,7 @@ private void configSso(SaTokenConfig cfg) {
// 配置 Http 请求处理器 // 配置 Http 请求处理器
cfg.sso.setSendHttp(url -> { cfg.sso.setSendHttp(url -> {
System.out.println("发起请求:" + url);
return OkHttps.sync(url).get().getBody().toString(); return OkHttps.sync(url).get().getBody().toString();
}); });
} }
@@ -70,12 +71,12 @@ sa-token:
``` java ``` java
// 自定义接口获取userinfo // 自定义接口获取userinfo
@RequestMapping("/sso/userinfo") @RequestMapping("/sso/userinfo")
public Object userinfo(String loginId, String secretkey) { public Object userinfo(String loginId) {
System.out.println("---------------- 获取userinfo --------"); System.out.println("---------------- 获取userinfo --------");
// 校验调用秘钥 // 校验签名,防止敏感信息外泄
SaSsoUtil.checkSecretkey(secretkey); SaSsoUtil.checkSign(SaHolder.getRequest());
// 自定义返回结果(模拟) // 自定义返回结果(模拟)
return SaResult.ok() return SaResult.ok()
.set("id", loginId) .set("id", loginId)

View File

@@ -4,7 +4,6 @@ import java.util.Map;
import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWT;
@@ -53,9 +52,6 @@ public class SaJwtUtil {
*/ */
public static String createToken(Object loginId, Map<String, Object> extraData, String keyt) { public static String createToken(Object loginId, Map<String, Object> extraData, String keyt) {
// 秘钥不可以为空
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
// 构建 // 构建
String token = JWT.create() String token = JWT.create()
.setPayload(LOGIN_ID, loginId) .setPayload(LOGIN_ID, loginId)
@@ -82,9 +78,6 @@ public class SaJwtUtil {
public static String createToken(String loginType, Object loginId, String device, public static String createToken(String loginType, Object loginId, String device,
long timeout, Map<String, Object> extraData, String keyt) { long timeout, Map<String, Object> extraData, String keyt) {
// 秘钥不可以为空
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
// 计算有效期 // 计算有效期
long effTime = timeout; long effTime = timeout;
if(timeout != NEVER_EXPIRE) { if(timeout != NEVER_EXPIRE) {
@@ -112,9 +105,7 @@ public class SaJwtUtil {
* @return 解析后的jwt 对象 * @return 解析后的jwt 对象
*/ */
public static JWT parseToken(String token, String keyt) { public static JWT parseToken(String token, String keyt) {
// 秘钥不可以为空
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
// 如果token为null // 如果token为null
if(token == null) { if(token == null) {
throw NotLoginException.newInstance(null, NotLoginException.NOT_TOKEN); throw NotLoginException.newInstance(null, NotLoginException.NOT_TOKEN);

View File

@@ -7,6 +7,7 @@ import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException; import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -38,7 +39,9 @@ public class StpLogicJwtForMix extends StpLogic {
* @return / * @return /
*/ */
public String jwtSecretKey() { public String jwtSecretKey() {
return getConfig().getJwtSecretKey(); String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
} }
// //

View File

@@ -40,7 +40,9 @@ public class StpLogicJwtForStateless extends StpLogic {
* @return / * @return /
*/ */
public String jwtSecretKey() { public String jwtSecretKey() {
return getConfig().getJwtSecretKey(); String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
} }
// //

View File

@@ -2,6 +2,7 @@ package cn.dev33.satoken.jwt;
import java.util.Map; import java.util.Map;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -32,7 +33,9 @@ public class StpLogicJwtForStyle extends StpLogic {
* @return / * @return /
*/ */
public String jwtSecretKey() { public String jwtSecretKey() {
return getConfig().getJwtSecretKey(); String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
} }
// ------ 重写方法 // ------ 重写方法

View File

@@ -91,6 +91,14 @@ public class SaSsoConfig implements Serializable {
public String ssoLogoutCall; public String ssoLogoutCall;
// ----------------- 其它
/**
* 接口调用时的时间戳允许的差距单位ms-1代表不校验差距
*/
public long timestampDisparity = 1000 * 60 * 10;
/** /**
@@ -253,12 +261,37 @@ public class SaSsoConfig implements Serializable {
return this; 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 @Override
public String toString() { public String toString() {
return "SaSsoConfig [ticketTimeout=" + ticketTimeout + ", allowUrl=" + allowUrl + ", isSlo=" + isSlo return "SaSsoConfig ["
+ ", isHttp=" + isHttp + ", secretkey=" + secretkey + ", authUrl=" + authUrl + ", checkTicketUrl=" + "ticketTimeout=" + ticketTimeout
+ checkTicketUrl + ", userinfoUrl=" + userinfoUrl + ", sloUrl=" + sloUrl + ", ssoLogoutCall=" + ", allowUrl=" + allowUrl
+ ssoLogoutCall + "]"; + ", isSlo=" + isSlo
+ ", isHttp=" + isHttp
+ ", secretkey=" + secretkey
+ ", authUrl=" + authUrl
+ ", checkTicketUrl=" + checkTicketUrl
+ ", userinfoUrl=" + userinfoUrl
+ ", sloUrl=" + sloUrl
+ ", ssoLogoutCall=" + ssoLogoutCall
+ ", timestampDisparity=" + timestampDisparity
+ "]";
} }
/** /**

View File

@@ -62,6 +62,10 @@ public class SaSsoConsts {
public static String name = "name"; public static String name = "name";
public static String pwd = "pwd"; public static String pwd = "pwd";
public static String timestamp = "timestamp";
public static String nonce = "nonce";
public static String sign = "sign";
} }

View File

@@ -1,8 +1,5 @@
package cn.dev33.satoken.sso; package cn.dev33.satoken.sso;
import java.util.Map;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaSsoConfig; import cn.dev33.satoken.config.SaSsoConfig;
import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.context.model.SaRequest;
@@ -110,15 +107,13 @@ public class SaSsoHandle {
} }
/** /**
* SSO-Server端校验ticket 获取账号id * SSO-Server端校验ticket 获取账号id [模式三]
* @return 处理结果 * @return 处理结果
*/ */
public static Object ssoCheckTicket() { public static Object ssoCheckTicket() {
// 获取对象
SaRequest req = SaHolder.getRequest();
// 获取参数 // 获取参数
String ticket = req.getParam(ParamName.ticket); SaRequest req = SaHolder.getRequest();
String ticket = req.getParamNotNull(ParamName.ticket);
String sloCallback = req.getParam(ParamName.ssoLogoutCall); String sloCallback = req.getParam(ParamName.ssoLogoutCall);
// 校验ticket获取 loginId // 校验ticket获取 loginId
@@ -143,15 +138,12 @@ public class SaSsoHandle {
// 获取对象 // 获取对象
SaRequest req = SaHolder.getRequest(); SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse(); SaResponse res = SaHolder.getResponse();
SaSsoConfig cfg = SaSsoManager.getConfig(); Object loginId = SaSsoUtil.saSsoTemplate.stpLogic.getLoginIdDefaultNull();
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
String loginId = req.getParam(ParamName.loginId);
// step.1 遍历 Client 端注销 // 单点注销
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.getSendHttp().apply(url)); if(SaFoxUtil.isNotEmpty(loginId)) {
SaSsoUtil.ssoLogout(loginId);
// step.2 Server 端注销 }
stpLogic.logout();
// 完成 // 完成
return ssoLogoutBack(req, res); return ssoLogoutBack(req, res);
@@ -162,25 +154,17 @@ public class SaSsoHandle {
* @return 处理结果 * @return 处理结果
*/ */
public static Object ssoLogoutByClientHttp() { public static Object ssoLogoutByClientHttp() {
// 获取对象
SaRequest req = SaHolder.getRequest();
SaSsoConfig cfg = SaSsoManager.getConfig();
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
// 获取参数 // 获取参数
SaRequest req = SaHolder.getRequest();
String loginId = req.getParam(ParamName.loginId); String loginId = req.getParam(ParamName.loginId);
String secretkey = req.getParam(ParamName.secretkey);
// step.1 校验秘钥 // step.1 校验签名
SaSsoUtil.checkSecretkey(secretkey); SaSsoUtil.checkSign(req);
// step.2 遍历 Client 端注销 // step.2 单点注销
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.getSendHttp().apply(url)); SaSsoUtil.ssoLogout(loginId);
// step.3 Server 端注销 // 响应
stpLogic.logout(loginId);
// 完成
return SaResult.ok(); return SaResult.ok();
} }
@@ -302,12 +286,16 @@ public class SaSsoHandle {
return SaResult.ok(); return SaResult.ok();
} }
// 调用SSO-Server认证中心API进行注销 // 调用 sso-server 认证中心单点注销API
String url = SaSsoUtil.buildSloUrl(stpLogic.getLoginId()); String url = SaSsoUtil.buildSloUrl(stpLogic.getLoginId());
SaResult result = request(url); SaResult result = SaSsoUtil.request(url);
// 校验 // 校验响应状态码
if(result.getCode() == SaResult.CODE_SUCCESS) { if(result.getCode() == SaResult.CODE_SUCCESS) {
// 极端场景下sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀
if(stpLogic.isLogin()) {
stpLogic.logout();
}
return ssoLogoutBack(req, res); return ssoLogoutBack(req, res);
} else { } else {
// 将 sso-server 回应的消息作为异常抛出 // 将 sso-server 回应的消息作为异常抛出
@@ -325,11 +313,10 @@ public class SaSsoHandle {
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic; StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
// 获取参数 // 获取参数
String loginId = req.getParam(ParamName.loginId); String loginId = req.getParamNotNull(ParamName.loginId);
String secretkey = req.getParam(ParamName.secretkey);
// 注销当前应用端会话 // 注销当前应用端会话
SaSsoUtil.checkSecretkey(secretkey); SaSsoUtil.checkSign(req);
stpLogic.logout(loginId); stpLogic.logout(loginId);
// 响应 // 响应
@@ -382,7 +369,7 @@ public class SaSsoHandle {
// 发起请求 // 发起请求
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall); String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
SaResult result = request(checkUrl); SaResult result = SaSsoUtil.request(checkUrl);
// 校验 // 校验
if(result.getCode() == SaResult.CODE_SUCCESS) { if(result.getCode() == SaResult.CODE_SUCCESS) {
@@ -397,15 +384,4 @@ public class SaSsoHandle {
} }
} }
/**
* 发出请求,并返回 SaResult 结果
* @param url 请求地址
* @return 返回的结果
*/
public static SaResult request(String url) {
String body = SaSsoManager.getConfig().getSendHttp().apply(url);
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(body);
return new SaResult(map);
}
} }

View File

@@ -3,10 +3,13 @@ package cn.dev33.satoken.sso;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap;
import cn.dev33.satoken.SaManager; import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaSsoConfig; import cn.dev33.satoken.config.SaSsoConfig;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.sso.SaSsoConsts.ParamName; import cn.dev33.satoken.sso.SaSsoConsts.ParamName;
import cn.dev33.satoken.sso.exception.SaSsoException; import cn.dev33.satoken.sso.exception.SaSsoException;
@@ -14,6 +17,7 @@ import cn.dev33.satoken.sso.exception.SaSsoExceptionCode;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
/** /**
* Sa-Token-SSO 单点登录模块 * Sa-Token-SSO 单点登录模块
@@ -149,6 +153,94 @@ public class SaSsoTemplate {
return SaFoxUtil.getRandomString(64); return SaFoxUtil.getRandomString(64);
} }
/**
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* @return see note
*/
public String getAllowUrl() {
// 默认从配置文件中返回
return SaSsoManager.getConfig().getAllowUrl();
}
/**
* 校验重定向url合法性
* @param url 下放ticket的url地址
*/
public void checkRedirectUrl(String url) {
// 1、是否是一个有效的url
if(SaFoxUtil.isUrl(url) == false) {
throw new SaSsoException("无效redirect" + url).setCode(SaSsoExceptionCode.CODE_20001);
}
// 2、截取掉?后面的部分
int qIndex = url.indexOf("?");
if(qIndex != -1) {
url = url.substring(0, qIndex);
}
// 3、是否在[允许地址列表]之中
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
if(SaStrategy.me.hasElement.apply(authUrlList, url) == false) {
throw new SaSsoException("非法redirect" + url).setCode(SaSsoExceptionCode.CODE_20002);
}
// 校验通过 √
return;
}
// ------------------- SSO 模式三相关 -------------------
/**
* 为指定账号id注册单点注销回调URL
* @param loginId 账号id
* @param sloCallbackUrl 单点注销时的回调URL
*/
public void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) {
if(SaFoxUtil.isEmpty(loginId) || SaFoxUtil.isEmpty(sloCallbackUrl)) {
return;
}
SaSession session = stpLogic.getSessionByLoginId(loginId);
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, ()-> new HashSet<String>());
urlSet.add(sloCallbackUrl);
session.set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet);
}
/**
* 指定账号单点注销
* @param loginId 指定账号
*/
public void ssoLogout(Object loginId) {
// 如果这个账号尚未登录,则无操作
SaSession session = stpLogic.getSessionByLoginId(loginId, false);
if(session == null) {
return;
}
// step.1 遍历通知 Client 端注销会话
SaSsoConfig cfg = SaSsoManager.getConfig();
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, () -> new HashSet<String>());
for (String url : urlSet) {
url = addSignParams(url, loginId);
cfg.getSendHttp().apply(url);
}
// step.2 Server端注销
stpLogic.logout(loginId);
}
/**
* 获取:账号资料
* @param loginId 账号id
* @return 账号资料
*/
public Object getUserinfo(Object loginId) {
String url = buildUserinfoUrl(loginId);
return SaSsoManager.getConfig().getSendHttp().apply(url);
}
// ---------------------- 构建URL ---------------------- // ---------------------- 构建URL ----------------------
@@ -202,43 +294,7 @@ public class SaSsoTemplate {
// 构建 授权重定向地址 Server端 根据此地址向 Client端 下放Ticket // 构建 授权重定向地址 Server端 根据此地址向 Client端 下放Ticket
return SaFoxUtil.joinParam(encodeBackParam(redirect), ParamName.ticket, ticket); return SaFoxUtil.joinParam(encodeBackParam(redirect), ParamName.ticket, ticket);
} }
/**
* 校验重定向url合法性
* @param url 下放ticket的url地址
*/
public void checkRedirectUrl(String url) {
// 1、是否是一个有效的url
if(SaFoxUtil.isUrl(url) == false) {
throw new SaSsoException("无效redirect" + url).setCode(SaSsoExceptionCode.CODE_20001);
}
// 2、截取掉?后面的部分
int qIndex = url.indexOf("?");
if(qIndex != -1) {
url = url.substring(0, qIndex);
}
// 3、是否在[允许地址列表]之中
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
if(SaStrategy.me.hasElement.apply(authUrlList, url) == false) {
throw new SaSsoException("非法redirect" + url).setCode(SaSsoExceptionCode.CODE_20002);
}
// 校验通过 √
return;
}
/**
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* @return see note
*/
public String getAllowUrl() {
// 默认从配置文件中返回
return SaSsoManager.getConfig().getAllowUrl();
}
/** /**
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug * 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
* @param url url * @param url url
@@ -271,27 +327,10 @@ public class SaSsoTemplate {
* @return Server端 账号资料查询地址 * @return Server端 账号资料查询地址
*/ */
public String buildUserinfoUrl(Object loginId) { public String buildUserinfoUrl(Object loginId) {
// 拼接
String userinfoUrl = SaSsoManager.getConfig().getUserinfoUrl(); String userinfoUrl = SaSsoManager.getConfig().getUserinfoUrl();
userinfoUrl = SaFoxUtil.joinParam(userinfoUrl, ParamName.loginId, loginId); return addSignParams(userinfoUrl, loginId);
userinfoUrl = SaFoxUtil.joinParam(userinfoUrl, ParamName.secretkey, SaSsoManager.getConfig().getSecretkey());
// 返回
return userinfoUrl;
} }
// ------------------- SSO 模式三相关 -------------------
/**
* 校验secretkey秘钥是否有效
* @param secretkey 秘钥
*/
public void checkSecretkey(String secretkey) {
if(secretkey == null || secretkey.isEmpty() || secretkey.equals(SaSsoManager.getConfig().getSecretkey()) == false) {
throw new SaSsoException("无效秘钥:" + secretkey).setCode(SaSsoExceptionCode.CODE_20003);
}
}
/** /**
* 构建URL校验ticket的URL * 构建URL校验ticket的URL
* <p> 在模式三下Client端拿到Ticket后根据此地址向Server端发送请求获取账号id * <p> 在模式三下Client端拿到Ticket后根据此地址向Server端发送请求获取账号id
@@ -314,43 +353,6 @@ public class SaSsoTemplate {
// 返回 // 返回
return url; return url;
} }
/**
* 为指定账号id注册单点注销回调URL
* @param loginId 账号id
* @param sloCallbackUrl 单点注销时的回调URL
*/
public void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) {
if(loginId == null || sloCallbackUrl == null || sloCallbackUrl.isEmpty()) {
return;
}
SaSession session = stpLogic.getSessionByLoginId(loginId);
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, ()-> new HashSet<String>());
urlSet.add(sloCallbackUrl);
session.set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet);
}
/**
* 循环调用Client端单点注销回调
* @param loginId 账号id
* @param fun 调用方法
*/
public void forEachSloUrl(Object loginId, CallSloUrlFunction fun) {
SaSession session = stpLogic.getSessionByLoginId(loginId, false);
if(session == null) {
return;
}
String secretkey = SaSsoManager.getConfig().getSecretkey();
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, () -> new HashSet<String>());
for (String url : urlSet) {
// 拼接login参数、秘钥参数
url = SaFoxUtil.joinParam(url, ParamName.loginId, loginId);
url = SaFoxUtil.joinParam(url, ParamName.secretkey, secretkey);
// 调用
fun.run(url);
}
}
/** /**
* 构建URL单点注销URL * 构建URL单点注销URL
@@ -358,42 +360,10 @@ public class SaSsoTemplate {
* @return 单点注销URL * @return 单点注销URL
*/ */
public String buildSloUrl(Object loginId) { public String buildSloUrl(Object loginId) {
SaSsoConfig ssoConfig = SaSsoManager.getConfig(); String url = SaSsoManager.getConfig().getSloUrl();
String url = ssoConfig.getSloUrl(); return addSignParams(url, loginId);
url = SaFoxUtil.joinParam(url, ParamName.loginId, loginId);
url = SaFoxUtil.joinParam(url, ParamName.secretkey, ssoConfig.getSecretkey());
return url;
}
/**
* 指定账号单点注销
* @param secretkey 校验秘钥
* @param loginId 指定账号
* @param fun 调用方法
*/
public void singleLogout(String secretkey, Object loginId, CallSloUrlFunction fun) {
// step.1 校验秘钥
checkSecretkey(secretkey);
// step.2 遍历通知Client端注销会话
forEachSloUrl(loginId, fun);
// step.3 Server端注销
// StpUtil.logoutByLoginId(loginId);
stpLogic.logoutByTokenValue(stpLogic.getTokenValueByLoginId(loginId));
} }
/**
* 获取:账号资料
* @param loginId 账号id
* @return 账号资料
*/
public Object getUserinfo(Object loginId) {
String url = buildUserinfoUrl(loginId);
return SaSsoManager.getConfig().getSendHttp().apply(url);
}
// ------------------- 返回相应key ------------------- // ------------------- 返回相应key -------------------
@@ -415,17 +385,112 @@ public class SaSsoTemplate {
return SaManager.getConfig().getTokenName() + ":id-ticket:" + id; return SaManager.getConfig().getTokenName() + ":id-ticket:" + id;
} }
// ------------------- 请求相关 -------------------
/** /**
* 单点注销回调函数 * 发出请求,并返回 SaResult 结果
* @author kong * @param url 请求地址
* @return 返回的结果
*/ */
@FunctionalInterface public SaResult request(String url) {
public static interface CallSloUrlFunction{ String body = SaSsoManager.getConfig().getSendHttp().apply(url);
/** Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(body);
* 调用function return new SaResult(map);
* @param url 注销回调URL }
*/
public void run(String url); /**
* 获取:接口调用秘钥
* @return see note
*/
public String getSecretkey() {
// 默认从配置文件中返回
String secretkey = SaSsoManager.getConfig().getSecretkey();
if(SaFoxUtil.isEmpty(secretkey)) {
throw new SaSsoException("请配置 secretkey 参数").setCode(SaSsoExceptionCode.CODE_20009);
}
return secretkey;
}
/**
* 校验secretkey秘钥是否有效 API已过期请更改为更安全的 sign 式校验)
* @param secretkey 秘钥
*/
@Deprecated
public void checkSecretkey(String secretkey) {
if(SaFoxUtil.isEmpty(secretkey) || secretkey.equals(getSecretkey()) == false) {
throw new SaSsoException("无效秘钥:" + secretkey).setCode(SaSsoExceptionCode.CODE_20003);
}
}
/**
* 根据参数计算签名
* @param loginId 账号id
* @param timestamp 当前时间戳13位
* @param nonce 随机字符串
* @param secretkey 账号id
* @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);
}
/**
* 给 url 追加 sign 等参数
* @param loginId
* @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(SaSsoExceptionCode.CODE_20008);
}
}
/**
* 校验时间戳与当前时间的差距是否超出限制
* @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(SaSsoExceptionCode.CODE_20007);
}
} }
} }

View File

@@ -1,7 +1,8 @@
package cn.dev33.satoken.sso; package cn.dev33.satoken.sso;
import cn.dev33.satoken.sso.SaSsoTemplate.CallSloUrlFunction; import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/** /**
* Sa-Token-SSO 单点登录模块 工具类 * Sa-Token-SSO 单点登录模块 工具类
@@ -71,7 +72,71 @@ public class SaSsoUtil {
public static Object checkTicket(String ticket) { public static Object checkTicket(String ticket) {
return saSsoTemplate.checkTicket(ticket); return saSsoTemplate.checkTicket(ticket);
} }
/**
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* @return see note
*/
public static String getAllowUrl() {
return saSsoTemplate.getAllowUrl();
}
/**
* 校验重定向url合法性
* @param url 下放ticket的url地址
*/
public static void checkRedirectUrl(String url) {
saSsoTemplate.checkRedirectUrl(url);
}
// ------------------- SSO 模式三 -------------------
/**
* 构建URL校验ticket的URL
* @param ticket ticket码
* @param ssoLogoutCallUrl 单点注销时的回调URL
* @return 构建完毕的URL
*/
public static String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
return saSsoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl);
}
/**
* 为指定账号id注册单点注销回调URL
* @param loginId 账号id
* @param sloCallbackUrl 单点注销时的回调URL
*/
public static void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) {
saSsoTemplate.registerSloCallbackUrl(loginId, sloCallbackUrl);
}
/**
* 构建URL单点注销URL
* @param loginId 要注销的账号id
* @return 单点注销URL
*/
public static String buildSloUrl(Object loginId) {
return saSsoTemplate.buildSloUrl(loginId);
}
/**
* 指定账号单点注销
* @param loginId 指定账号
*/
public static void ssoLogout(Object loginId) {
saSsoTemplate.ssoLogout(loginId);
}
/**
* 获取:账号资料
* @param loginId 账号id
* @return 账号资料
*/
public static Object getUserinfo(Object loginId) {
return saSsoTemplate.getUserinfo(loginId);
}
// ---------------------- 构建URL ---------------------- // ---------------------- 构建URL ----------------------
@@ -94,22 +159,6 @@ public class SaSsoUtil {
public static String buildRedirectUrl(Object loginId, String redirect) { public static String buildRedirectUrl(Object loginId, String redirect) {
return saSsoTemplate.buildRedirectUrl(loginId, redirect); return saSsoTemplate.buildRedirectUrl(loginId, redirect);
} }
/**
* 校验重定向url合法性
* @param url 下放ticket的url地址
*/
public static void checkRedirectUrl(String url) {
saSsoTemplate.checkRedirectUrl(url);
}
/**
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* @return see note
*/
public static String getAllowUrl() {
return saSsoTemplate.getAllowUrl();
}
/** /**
* 构建URLServer端 账号资料查询地址 * 构建URLServer端 账号资料查询地址
@@ -120,70 +169,62 @@ public class SaSsoUtil {
return saSsoTemplate.buildUserinfoUrl(loginId); return saSsoTemplate.buildUserinfoUrl(loginId);
} }
// ------------------- SSO 模式三 -------------------
// ------------------- 请求相关 -------------------
/** /**
* 校验secretkey秘钥是否有效 * 发出请求,并返回 SaResult 结果
* @param url 请求地址
* @return 返回的结果
*/
public static SaResult request(String url) {
return saSsoTemplate.request(url);
}
/**
* 校验secretkey秘钥是否有效 API已过期请更改为更安全的 sign 式校验)
* @param secretkey 秘钥 * @param secretkey 秘钥
*/ */
@Deprecated
public static void checkSecretkey(String secretkey) { public static void checkSecretkey(String secretkey) {
saSsoTemplate.checkSecretkey(secretkey); saSsoTemplate.checkSecretkey(secretkey);
} }
/** /**
* 构建URL校验ticket的URL * 根据参数计算签名
* @param ticket ticket码
* @param ssoLogoutCallUrl 单点注销时的回调URL
* @return 构建完毕的URL
*/
public static String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
return saSsoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl);
}
/**
* 为指定账号id注册单点注销回调URL
* @param loginId 账号id * @param loginId 账号id
* @param sloCallbackUrl 单点注销时的回调URL * @param timestamp 当前时间戳13位
* @param nonce 随机字符串
* @param secretkey 账号id
* @return 签名
*/ */
public static void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) { public static String getSign(Object loginId, String timestamp, String nonce, String secretkey) {
saSsoTemplate.registerSloCallbackUrl(loginId, sloCallbackUrl); return saSsoTemplate.getSign(loginId, timestamp, nonce, secretkey);
} }
/** /**
* 循环调用Client端单点注销回调 * 给 url 追加 sign 等参数
* @param loginId 账号id * @param loginId
* @param fun 调用方法 * @return 加工后的url
*/ */
public static void forEachSloUrl(Object loginId, CallSloUrlFunction fun) { public static String addSignParams(String url, Object loginId) {
saSsoTemplate.forEachSloUrl(loginId, fun); return saSsoTemplate.addSignParams(url, loginId);
} }
/** /**
* 构建URL单点注销URL * 校验签名
* @param loginId 要注销的账号id * @param req request
* @return 单点注销URL
*/ */
public static String buildSloUrl(Object loginId) { public static void checkSign(SaRequest req) {
return saSsoTemplate.buildSloUrl(loginId); saSsoTemplate.checkSign(req);
} }
/** /**
* 指定账号单点注销 * 校验时间戳与当前时间的差距是否超出限制
* @param secretkey 校验秘钥 * @param timestamp 时间戳
* @param loginId 指定账号
* @param fun 调用方法
*/ */
public static void singleLogout(String secretkey, Object loginId, CallSloUrlFunction fun) { public static void checkTimestamp(long timestamp) {
saSsoTemplate.singleLogout(secretkey, loginId, fun); saSsoTemplate.checkTimestamp(timestamp);
}
/**
* 获取:账号资料
* @param loginId 账号id
* @return 账号资料
*/
public static Object getUserinfo(Object loginId) {
return saSsoTemplate.getUserinfo(loginId);
} }
} }

View File

@@ -25,5 +25,14 @@ public class SaSsoExceptionCode {
/** 在模式三下sso-client 调用 sso-server 端 单点注销接口 时,得到的响应是注销失败 */ /** 在模式三下sso-client 调用 sso-server 端 单点注销接口 时,得到的响应是注销失败 */
public static final int CODE_20006 = 20006; public static final int CODE_20006 = 20006;
/** http 请求调用 提供的 timestamp 与当前时间的差距超出允许的范围 */
public static final int CODE_20007 = 20007;
/** http 请求调用 提供的 sign 无效 */
public static final int CODE_20008 = 20008;
/** 本地系统没有配置 secretkey 字段 */
public static final int CODE_20009 = 20009;
} }

View File

@@ -15,6 +15,7 @@ import cn.dev33.satoken.id.SaIdTemplate;
import cn.dev33.satoken.id.SaIdUtil; import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.json.SaJsonTemplate; import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -127,6 +128,16 @@ public class SaBeanInject {
public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) { public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
SaManager.setSaJsonTemplate(saJsonTemplate); SaManager.setSaJsonTemplate(saJsonTemplate);
} }
/**
* 注入自定义的 参数签名 Bean
*
* @param saSignTemplate 参数签名 Bean
*/
@Autowired(required = false)
public void setSaSignTemplate(SaSignTemplate saSignTemplate) {
SaManager.setSaSignTemplate(saSignTemplate);
}
/** /**
* 注入自定义的 StpLogic * 注入自定义的 StpLogic

View File

@@ -18,7 +18,9 @@ import cn.dev33.satoken.context.second.SaTokenSecondContextCreator;
import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.id.SaIdTemplate; import cn.dev33.satoken.id.SaIdTemplate;
import cn.dev33.satoken.id.SaIdUtil; import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.solon.integration.SaContextForSolon; import cn.dev33.satoken.solon.integration.SaContextForSolon;
import cn.dev33.satoken.solon.integration.SaTokenMethodInterceptor; import cn.dev33.satoken.solon.integration.SaTokenMethodInterceptor;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
@@ -90,6 +92,16 @@ public class XPluginImp implements Plugin {
SaBasicUtil.saBasicTemplate = bw.raw(); SaBasicUtil.saBasicTemplate = bw.raw();
}); });
// Sa-Token JSON 转换器 Bean
Aop.getAsyn(SaJsonTemplate.class, bw->{
SaManager.setSaJsonTemplate(bw.raw());
});
// Sa-Token 参数签名算法 Bean
Aop.getAsyn(SaSignTemplate.class, bw->{
SaManager.setSaSignTemplate(bw.raw());
});
// 自定义 StpLogic 对象 // 自定义 StpLogic 对象
Aop.getAsyn(StpLogic.class, bw->{ Aop.getAsyn(StpLogic.class, bw->{
StpUtil.setStpLogic(bw.raw()); StpUtil.setStpLogic(bw.raw());

View File

@@ -15,6 +15,7 @@ import cn.dev33.satoken.id.SaIdTemplate;
import cn.dev33.satoken.id.SaIdUtil; import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.json.SaJsonTemplate; import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -127,6 +128,16 @@ public class SaBeanInject {
public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) { public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
SaManager.setSaJsonTemplate(saJsonTemplate); SaManager.setSaJsonTemplate(saJsonTemplate);
} }
/**
* 注入自定义的 参数签名 Bean
*
* @param saSignTemplate 参数签名 Bean
*/
@Autowired(required = false)
public void setSaSignTemplate(SaSignTemplate saSignTemplate) {
SaManager.setSaSignTemplate(saSignTemplate);
}
/** /**
* 注入自定义的 StpLogic * 注入自定义的 StpLogic