feat: sa-token-sso 模块新增消息推送机制

This commit is contained in:
click33 2025-04-30 03:55:42 +08:00
parent b53eac9269
commit 95cc77a8bc
26 changed files with 1210 additions and 608 deletions

View File

@ -57,12 +57,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Http请求工具在模式三的单点注销功能下用到如不需要可以注释掉 -->
<!-- Sa-Token 插件:整合 Forest 请求工具 -->
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.26</version>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-forest</artifactId>
<version>${sa-token.version}</version>
</dependency>

View File

@ -6,7 +6,6 @@ import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
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;
@ -50,19 +49,7 @@ public class SsoServerController {
}
return SaResult.error("登录失败!");
};
// 配置 Http 请求处理器 在模式三的单点注销功能下用到如不需要可以注释掉
ssoServer.sendHttp = url -> {
try {
System.out.println("------ 发起请求:" + url);
String resStr = Forest.get(url).executeAsString();
System.out.println("------ 请求结果:" + resStr);
return resStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
};
}
// 示例获取数据接口用于在模式三下 client 端开放拉取数据的接口

View File

@ -35,8 +35,9 @@ sa-token:
# 应用 sso-client3采用模式三对接 (跨域、跨Redis)
sso-client3:
client: sso-client3
secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
allow-url: "*"
secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
push-url: http://sa-sso-client1.com:9003/sso/pushC
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器文档有步骤说明

View File

@ -5,7 +5,6 @@ import cn.dev33.satoken.sso.config.SaSsoClientConfig;
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
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;
@ -43,13 +42,7 @@ public class SsoClientController {
// 配置SSO相关参数
@Autowired
private void configSso(SaSsoClientConfig ssoClient) {
// 配置Http请求处理器
ssoClient.sendHttp = url -> {
System.out.println("------ 发起请求:" + url);
String resStr = Forest.get(url).executeAsString();
System.out.println("------ 请求结果:" + resStr);
return resStr;
};
}
// 全局异常拦截

View File

@ -53,15 +53,16 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Http请求工具 -->
<!-- Sa-Token 插件:整合 Forest 请求工具 -->
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.26</version>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-forest</artifactId>
<version>${sa-token.version}</version>
</dependency>
</dependencies>

View File

@ -5,7 +5,6 @@ import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
import cn.dev33.satoken.sso.template.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;
@ -45,13 +44,7 @@ public class SsoClientController {
// 配置SSO相关参数
@Autowired
private void configSso(SaSsoClientConfig ssoClient) {
// 配置Http请求处理器
ssoClient.sendHttp = url -> {
System.out.println("------ 发起请求:" + url);
String resStr = Forest.get(url).executeAsString();
System.out.println("------ 请求结果:" + resStr);
return resStr;
};
}
// 查询我的账号信息

View File

@ -21,7 +21,7 @@ import com.dtflys.forest.Forest;
import java.util.Map;
/**
* Http 转换 Forest 版实现
* Http 请求处理 Forest 版实现
*
* @author click33
* @since 1.43.0

View File

@ -17,6 +17,7 @@ package cn.dev33.satoken.plugin;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.http.SaHttpTemplateForForest;
import com.dtflys.forest.config.ForestConfiguration;
/**
* SaToken 插件安装Http 请求处理器 - Forest
@ -28,6 +29,10 @@ public class SaTokenPluginForForest implements SaTokenPlugin {
@Override
public void install() {
// 关闭 Forest 默认日志打印
ForestConfiguration.getDefaultConfiguration().setLogEnabled(false);
// 设置 Forest 作为 Http 请求处理器
SaManager.setSaHttpTemplate(new SaHttpTemplateForForest());
}

View File

@ -16,9 +16,6 @@
package cn.dev33.satoken.sso.config;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.function.SendHttpFunction;
import cn.dev33.satoken.sso.function.TicketResultHandleFunction;
import cn.dev33.satoken.util.SaFoxUtil;
@ -40,12 +37,12 @@ public class SaSsoClientConfig implements Serializable {
public String mode = "";
/**
* 当前 Client 名称标识用于和 ticket 码的互相锁定
* 当前 Client 标识
*/
public String client;
/**
* 配置 Server 端主机总地址拼接在 authUrlcheckTicketUrlgetDataUrlsloUrl 属性前面用以简化各种 url 配置
* 配置 Server 端主机总地址
*/
public String serverUrl;
@ -54,11 +51,6 @@ public class SaSsoClientConfig implements Serializable {
*/
public String authUrl = "/sso/auth";
/**
* 单独配置 Server 端的 ticket 校验地址
*/
public String checkTicketUrl = "/sso/checkTicket";
/**
* 单独配置 Server 端查询数据 getData 地址
*/
@ -69,6 +61,11 @@ public class SaSsoClientConfig implements Serializable {
*/
public String sloUrl = "/sso/signout";
/**
* 单独配置 Server 端推送消息地址
*/
public String pushUrl = "/sso/pushS";
/**
* 配置当前 Client 端的登录地址为空时自动获取
*/
@ -80,12 +77,17 @@ public class SaSsoClientConfig implements Serializable {
public String currSsoLogoutCall;
/**
* 是否打开单点注销功能
* 是否打开单点注销功能 ( true 接收单点注销回调消息推送)
*/
public Boolean isSlo = true;
/**
* 是否打开模式三此值为 true 时将使用 http 请求校验ticket值单点注销拉取数据getData
* 是否注册单点登录注销回调 ( true 登录时附带单点登录回调地址并且开放 /sso/logoutCall 地址)
*/
public Boolean regLogoutCall = false;
/**
* 是否打开模式三此值为 true 时将使用 http 请求校验 ticket
*/
public Boolean isHttp = false;
@ -100,6 +102,47 @@ public class SaSsoClientConfig implements Serializable {
public Boolean isCheckSign = true;
// 额外添加的一些函数
/**
* @return 获取拼接urlServer 端单点登录授权地址
*/
public String splicingAuthUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl());
}
/**
* @return 获取拼接urlServer 端查询数据 getData 地址
*/
public String splicingGetDataUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl());
}
/**
* @return 获取拼接urlServer 端单点注销地址
*/
public String splicingSloUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl());
}
/**
* @return 获取拼接url单独配置 Server 端推送消息地址
*/
public String splicingPushUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl());
}
// -------------------- 所有回调函数 --------------------
/**
* SSO-Client端自定义校验 ticket 返回值的处理逻辑 每次从认证中心获取校验 ticket 的结果后调用
* <p> 参数loginId, back
* <p> 返回值返回给前端的值
*/
public TicketResultHandleFunction ticketResultHandle = null;
// get set
/**
@ -183,22 +226,6 @@ public class SaSsoClientConfig implements Serializable {
return this;
}
/**
* @return 配置的 Server 端的 ticket 校验地址
*/
public String getCheckTicketUrl() {
return checkTicketUrl;
}
/**
* @param checkTicketUrl 配置 Server 端的 ticket 校验地址
* @return 对象自身
*/
public SaSsoClientConfig setCheckTicketUrl(String checkTicketUrl) {
this.checkTicketUrl = checkTicketUrl;
return this;
}
/**
* @return Server 端查询数据 getData 地址
*/
@ -231,6 +258,26 @@ public class SaSsoClientConfig implements Serializable {
return this;
}
/**
* 获取 单独配置 Server 端推送消息地址
*
* @return /
*/
public String getPushUrl() {
return this.pushUrl;
}
/**
* 设置 单独配置 Server 端推送消息地址
*
* @param pushUrl /
* @return 对象自身
*/
public SaSsoClientConfig setPushUrl(String pushUrl) {
this.pushUrl = pushUrl;
return this;
}
/**
* @return 配置当前 Client 端的登录地址为空时自动获取
*/
@ -318,6 +365,26 @@ public class SaSsoClientConfig implements Serializable {
return this;
}
/**
* 获取 是否注册单点登录注销回调 ( true 登录时附带单点登录回调地址并且开放 /sso/logoutCall 地址)
*
* @return /
*/
public Boolean getRegLogoutCall() {
return this.regLogoutCall;
}
/**
* 设置 是否注册单点登录注销回调 ( true 登录时附带单点登录回调地址并且开放 /sso/logoutCall 地址)
*
* @param regLogoutCall /
* @return /
*/
public SaSsoClientConfig setRegLogoutCall(Boolean regLogoutCall) {
this.regLogoutCall = regLogoutCall;
return this;
}
@Override
public String toString() {
return "SaSsoClientConfig ["
@ -325,63 +392,16 @@ public class SaSsoClientConfig implements Serializable {
+ ", client=" + client
+ ", serverUrl=" + serverUrl
+ ", authUrl=" + authUrl
+ ", checkTicketUrl=" + checkTicketUrl
+ ", getDataUrl=" + getDataUrl
+ ", sloUrl=" + sloUrl
+ ", currSsoLogin=" + currSsoLogin
+ ", currSsoLogoutCall=" + currSsoLogoutCall
+ ", isSlo=" + isSlo
+ ", isHttp=" + isHttp
+ ", isSlo=" + isSlo
+ ", regLogoutCall=" + regLogoutCall
+ ", secretKey=" + secretKey
+ ", isCheckSign=" + isCheckSign
+ "]";
}
// 额外添加的一些函数
/**
* @return 获取拼接urlServer 端单点登录授权地址
*/
public String splicingAuthUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl());
}
/**
* @return 获取拼接urlServer 端的 ticket 校验地址
*/
public String splicingCheckTicketUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getCheckTicketUrl());
}
/**
* @return 获取拼接urlServer 端查询数据 getData 地址
*/
public String splicingGetDataUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl());
}
/**
* @return 获取拼接urlServer 端单点注销地址
*/
public String splicingSloUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl());
}
// -------------------- 所有回调函数 --------------------
/**
* SSO-Client端自定义校验 ticket 返回值的处理逻辑 每次从认证中心获取校验 ticket 的结果后调用
* <p> 参数loginId, back
* <p> 返回值返回给前端的值
*/
public TicketResultHandleFunction ticketResultHandle = null;
/**
* SSO-Client端发送Http请求的处理函数
*/
public SendHttpFunction sendHttp = url -> {
throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010);
};
}

View File

@ -42,6 +42,11 @@ public class SaSsoClientModel implements Serializable {
*/
public String allowUrl = "*";
/**
* 是否打开模式三此值为 true 时使用 http 调用方式进行消息通知
*/
public Boolean isHttp = false;
/**
* 是否打开单点注销功能
*/
@ -52,7 +57,18 @@ public class SaSsoClientModel implements Serializable {
*/
public String secretKey;
// 额外方法
/**
* Client 端主机总地址
*/
public String serverUrl;
/**
* Client 端推送消息的地址
*/
public String pushUrl = "/sso/pushC";
// 额外添加的一些函数
/**
* 以数组形式写入允许的授权回调地址
@ -64,6 +80,25 @@ public class SaSsoClientModel implements Serializable {
return this;
}
/**
* 获取拼接 url Client 端推送消息的地址
*
* @return /
*/
public String splicingNoticeUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl());
}
/**
* 判断是否配置了有效地推送地址
*
* @return /
*/
public boolean isValidNoticeUrl() {
return SaFoxUtil.isUrl(splicingNoticeUrl());
}
// get set
/**
@ -102,6 +137,22 @@ public class SaSsoClientModel implements Serializable {
return this;
}
/**
* @return isHttp 是否打开模式三
*/
public Boolean getIsHttp() {
return isHttp;
}
/**
* @param isHttp 是否打开模式三
* @return 对象自身
*/
public SaSsoClientModel setIsHttp(Boolean isHttp) {
this.isHttp = isHttp;
return this;
}
/**
* @return 是否打开单点注销功能
*/
@ -138,13 +189,56 @@ public class SaSsoClientModel implements Serializable {
return this;
}
/**
* 获取 Client 端主机总地址
*
* @return serverUrl Client 端主机总地址
*/
public String getServerUrl() {
return this.serverUrl;
}
/**
* 设置 Client 端主机总地址
*
* @param serverUrl Client 端主机总地址
* @return 对象自身
*/
public SaSsoClientModel setServerUrl(String serverUrl) {
this.serverUrl = serverUrl;
return this;
}
/**
* 获取 Client 端推送消息的地址
*
* @return noticeUrl Client 端推送消息的地址
*/
public String getPushUrl() {
return this.pushUrl;
}
/**
* 设置 Client 端推送消息的地址
*
* @param pushUrl Client 端推送消息的地址
* @return 对象自身
*/
public SaSsoClientModel setPushUrl(String pushUrl) {
this.pushUrl = pushUrl;
return this;
}
@Override
public String toString() {
return "SaSsoClientModel ["
+ "client=" + client
+ ", allowUrl=" + allowUrl
+ ", isSlo=" + isSlo
+ ", isHttp=" + isHttp
+ ", secretKey=" + secretKey
+ ", serverUrl=" + serverUrl
+ ", pushUrl=" + pushUrl
+ "]";
}

View File

@ -16,12 +16,9 @@
package cn.dev33.satoken.sso.config;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.function.CheckTicketAppendDataFunction;
import cn.dev33.satoken.sso.function.DoLoginHandleFunction;
import cn.dev33.satoken.sso.function.NotLoginViewFunction;
import cn.dev33.satoken.sso.function.SendHttpFunction;
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
@ -54,11 +51,6 @@ public class SaSsoServerConfig implements Serializable {
*/
public long ticketTimeout = 60 * 5;
/**
* 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
*/
public String allowUrl = "*";
/**
* 主页路由 /sso/auth 登录后不指定 redirect 参数的情况下默认跳转的路由
*/
@ -69,41 +61,43 @@ public class SaSsoServerConfig implements Serializable {
*/
public Boolean isSlo = true;
/**
* 是否打开模式三此值为 true 时将使用 http 请求校验ticket值单点注销获取userinfo
*/
public Boolean isHttp = false;
/**
* 是否在每次下发 ticket 自动续期 token 的有效期根据全局 timeout
*/
public Boolean autoRenewTimeout = false;
/**
* Access-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
* Account-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
*/
public int maxRegClient = 32;
/**
* 是否允许匿名 Client 接入
*/
public Boolean allowAnonClient = true;
/**
* 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true
*/
public Boolean isCheckSign = true;
/**
* API 调用签名秘钥
*/
public String secretKey;
/**
* Client 信息配置列表
*/
public Map<String, SaSsoClientModel> clients = new LinkedHashMap<>();
// 匿名 Client 相关配置
/**
* 是否允许匿名 Client 接入
*/
public Boolean allowAnonClient = true;
/**
* 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) (匿名 client 使用)
*/
public String allowUrl = "*";
/**
* API 调用签名秘钥 (全局默认 + 匿名 client 使用)
*/
public String secretKey;
// 额外方法
@ -128,6 +122,31 @@ public class SaSsoServerConfig implements Serializable {
}
// -------------------- 所有回调函数 --------------------
/**
* SSO-Server端未登录时返回的View
*/
public NotLoginViewFunction notLoginView = () -> {
return "当前会话在SSO-Server认证中心尚未登录当前未配置登录视图";
};
/**
* SSO-Server端登录函数
*/
public DoLoginHandleFunction doLoginHandle = (name, pwd) -> {
return SaResult.error();
};
/**
* SSO-Server端在校验 ticket sso-client 端追加返回信息的函数
*/
public CheckTicketAppendDataFunction checkTicketAppendData = (loginId, result) -> {
return result;
};
// get set
/**
@ -165,14 +184,14 @@ public class SaSsoServerConfig implements Serializable {
}
/**
* @return 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* @return 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) (匿名 client 使用)
*/
public String getAllowUrl() {
return allowUrl;
}
/**
* @param allowUrl 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* @param allowUrl 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) (匿名 client 使用)
* @return 对象自身
*/
public SaSsoServerConfig setAllowUrl(String allowUrl) {
@ -217,22 +236,6 @@ public class SaSsoServerConfig implements Serializable {
return this;
}
/**
* @return isHttp 是否打开模式三此值为 true 时将使用 http 请求校验ticket值单点注销获取userinfo
*/
public Boolean getIsHttp() {
return isHttp;
}
/**
* @param isHttp 是否打开模式三此值为 true 时将使用 http 请求校验ticket值单点注销获取userinfo
* @return 对象自身
*/
public SaSsoServerConfig setIsHttp(Boolean isHttp) {
this.isHttp = isHttp;
return this;
}
/**
* @return 是否在每次下发 ticket 自动续期 token 的有效期根据全局 timeout
*/
@ -250,14 +253,14 @@ public class SaSsoServerConfig implements Serializable {
}
/**
* @return maxLoginClient Access-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
* @return maxLoginClient Account-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
*/
public int getMaxRegClient() {
return maxRegClient;
}
/**
* @param maxRegClient Access-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
* @param maxRegClient Account-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
* @return 对象自身
*/
public SaSsoServerConfig setMaxRegClient(int maxRegClient) {
@ -304,7 +307,7 @@ public class SaSsoServerConfig implements Serializable {
}
/**
* 获取 API 调用签名秘钥
* 获取 API 调用签名秘钥 (全局默认 + 匿名 client 使用)
*
* @return /
*/
@ -313,7 +316,7 @@ public class SaSsoServerConfig implements Serializable {
}
/**
* 设置 API 调用签名秘钥
* 设置 API 调用签名秘钥 (全局默认 + 匿名 client 使用)
*
* @param secretKey /
* @return 对象自身
@ -351,7 +354,6 @@ public class SaSsoServerConfig implements Serializable {
+ ", allowUrl=" + allowUrl
+ ", homeRoute=" + homeRoute
+ ", isSlo=" + isSlo
+ ", isHttp=" + isHttp
+ ", autoRenewTimeout=" + autoRenewTimeout
+ ", maxRegClient=" + maxRegClient
+ ", isCheckSign=" + isCheckSign
@ -362,107 +364,4 @@ public class SaSsoServerConfig implements Serializable {
}
// -------------------- 所有回调函数 --------------------
/**
* SSO-Server端未登录时返回的View
*/
public NotLoginViewFunction notLoginView = () -> {
return "当前会话在SSO-Server认证中心尚未登录当前未配置登录视图";
};
/**
* SSO-Server端登录函数
*/
public DoLoginHandleFunction doLoginHandle = (name, pwd) -> {
return SaResult.error();
};
/**
* SSO-Server端在校验 ticket sso-client 端追加返回信息的函数
*/
public CheckTicketAppendDataFunction checkTicketAppendData = (loginId, result) -> {
return result;
};
/**
* SSO-Server端发送Http请求的处理函数
*/
public SendHttpFunction sendHttp = url -> {
throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010);
};
/**
* 获取 SSO-Server端未登录时返回的View
*
* @return notLoginView SSO-Server端未登录时返回的View
*/
public NotLoginViewFunction getNotLoginView() {
return this.notLoginView;
}
/**
* 设置 SSO-Server端未登录时返回的View
*
* @param notLoginView SSO-Server端未登录时返回的View
*/
public void setNotLoginView(NotLoginViewFunction notLoginView) {
this.notLoginView = notLoginView;
}
/**
* 获取 SSO-Server端登录函数
*
* @return doLoginHandle SSO-Server端登录函数
*/
public DoLoginHandleFunction getDoLoginHandle() {
return this.doLoginHandle;
}
/**
* 设置 SSO-Server端登录函数
*
* @param doLoginHandle SSO-Server端登录函数
*/
public void setDoLoginHandle(DoLoginHandleFunction doLoginHandle) {
this.doLoginHandle = doLoginHandle;
}
/**
* 获取 SSO-Server端在校验 ticket sso-client 端追加返回信息的函数
*
* @return checkTicketAppendData SSO-Server端在校验 ticket sso-client 端追加返回信息的函数
*/
public CheckTicketAppendDataFunction getCheckTicketAppendData() {
return this.checkTicketAppendData;
}
/**
* 设置 SSO-Server端在校验 ticket sso-client 端追加返回信息的函数
*
* @param checkTicketAppendData SSO-Server端在校验 ticket sso-client 端追加返回信息的函数
*/
public void setCheckTicketAppendData(CheckTicketAppendDataFunction checkTicketAppendData) {
this.checkTicketAppendData = checkTicketAppendData;
}
/**
* 获取 SSO-Server端发送Http请求的处理函数
*
* @return sendHttp SSO-Server端发送Http请求的处理函数
*/
public SendHttpFunction getSendHttp() {
return this.sendHttp;
}
/**
* 设置 SSO-Server端发送Http请求的处理函数
*
* @param sendHttp SSO-Server端发送Http请求的处理函数
*/
public void setSendHttp(SendHttpFunction sendHttp) {
this.sendHttp = sendHttp;
}
}

View File

@ -68,4 +68,13 @@ public interface SaSsoErrorCode {
/** 无效的 allow-url 配置 */
int CODE_30015 = 30015;
/** 未能找到指定类型的消息处理器 */
int CODE_30021 = 30021;
/** 消息类型不能为空 */
int CODE_30022 = 30022;
/** 无效的消息推送地址 */
int CODE_30023 = 30023;
}

View File

@ -0,0 +1,111 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.sso.message;
import cn.dev33.satoken.application.SaSetValueInterface;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.util.SaFoxUtil;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-Token SSO 消息 Model
*
* @author click33
* @since 1.43.0
*/
public class SaSsoMessage extends LinkedHashMap<String, Object> implements SaSetValueInterface, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* KEYTYPE
*/
public static final String MSG_TYPE = "msg_type";
public SaSsoMessage() {
}
/**
* 构造函数
* @param type 消息类型
*/
public SaSsoMessage(String type) {
setType(type);
}
/**
* 构造函数
* @param map 消息参数
*/
public SaSsoMessage(Map<String, ?> map) {
this.putAll(map);
}
/**
* 获取消息类型
* @return /
*/
public String getType() {
return getString(MSG_TYPE);
}
/**
* 设置消息类型
* @param type /
* @return /
*/
public SaSsoMessage setType(String type) {
return set(MSG_TYPE, type);
}
/**
* 校验消息类型
*/
public void checkType() {
if(SaFoxUtil.isEmpty(getString(MSG_TYPE))) {
throw new SaSsoException("消息类型不可为空").setCode(SaSsoErrorCode.CODE_30022);
}
}
// -----------
@Override
public Object get(String key) {
return super.get(key);
}
@Override
public SaSsoMessage set(String key, Object value) {
super.put(key, value);
return this;
}
@Override
public SaSsoMessage delete(String key) {
super.remove(key);
return this;
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.sso.message;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
import cn.dev33.satoken.sso.template.SaSsoTemplate;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-Token SSO 消息处理器持有者
*
* @author click33
* @since 1.43.0
*/
public class SaSsoMessageHolder {
/**
* 所有消息处理器的集合
*/
public final Map<String, SaSsoMessageHandle> messageHandleMap = new LinkedHashMap<>();
/**
* 判断是否具有指定类型的消息处理器
*
* @param type 消息类型
* @return /
*/
public boolean hasHandle(String type) {
return messageHandleMap.containsKey(type);
}
/**
* 删除指定类型的消息处理器
*
* @param type 消息类型
*/
public SaSsoMessageHolder removeHandle(String type) {
messageHandleMap.remove(type);
return this;
}
/**
* 添加指定类型的消息处理器
*
* @param handle /
*/
public SaSsoMessageHolder addHandle(SaSsoMessageHandle handle) {
messageHandleMap.put(handle.getHandlerType(), handle);
return this;
}
/**
* 获取指定类型的消息处理器
*
* @param type /
*/
public SaSsoMessageHandle getHandle(String type) {
return messageHandleMap.get(type);
}
/**
* 处理指定消息
*
* @param ssoTemplate /
* @param message /
* @return /
*/
public Object handleMessage(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
SaSsoMessageHandle handle = messageHandleMap.get(message.getType());
if(handle == null) {
throw new SaSsoException("未能找到消息处理器: " + message.getType()).setCode(SaSsoErrorCode.CODE_30021);
}
return handle.handle(ssoTemplate, message);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.sso.message.handle;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.template.SaSsoTemplate;
/**
* Sa-Token SSO 消息 处理器
*
* @author click33
* @since 1.43.0
*/
public interface SaSsoMessageHandle {
/**
* 获取所要处理的消息类型
*
* @return /
*/
String getHandlerType();
/**
* 执行方法
*
* @param ssoTemplate /
* @param message /
* @return /
*/
Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message);
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.sso.message.handle.client;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.sso.template.SaSsoClientTemplate;
import cn.dev33.satoken.sso.template.SaSsoTemplate;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaResult;
/**
* Sa-Token SSO 消息 处理器 - sso-client 处理 单点注销回调 的请求
*
* @author click33
* @since 1.43.0
*/
public class SaSsoMessageLogoutCallHandle implements SaSsoMessageHandle {
/**
* 获取所要处理的消息类型
*
* @return /
*/
public String getHandlerType() {
return SaSsoConsts.MESSAGE_LOGOUT_CALL;
}
/**
* 执行方法
*
* @param ssoTemplate /
* @param message /
* @return /
*/
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
SaSsoClientTemplate ssoClientTemplate = (SaSsoClientTemplate) ssoTemplate;
if( ! ssoClientTemplate.getClientConfig().getIsSlo()) {
return SaResult.error("当前 sso-client 端未开启单点注销功能");
}
// 获取对象
SaRequest req = SaHolder.getRequest();
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
ParamName paramName = ssoClientTemplate.paramName;
// 获取参数
String loginId = req.getParamNotNull(paramName.loginId);
// 注销当前应用端会话
stpLogic.logout(loginId);
// 响应
return SaResult.ok("单点注销回调成功");
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.sso.message.handle.server;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
import cn.dev33.satoken.sso.template.SaSsoTemplate;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
/**
* Sa-Token SSO 消息 处理器 - sso-server 处理校验 ticket 的请求
*
* @author click33
* @since 1.43.0
*/
public class SaSsoMessageCheckTicketHandle implements SaSsoMessageHandle {
/**
* 获取所要处理的消息类型
*
* @return /
*/
public String getHandlerType() {
return SaSsoConsts.MESSAGE_CHECK_TICKET;
}
/**
* 执行方法
*
* @param ssoTemplate /
* @param message /
* @return /
*/
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate;
ParamName paramName = ssoServerTemplate.paramName;
// 1获取参数
SaRequest req = SaHolder.getRequest();
SaSsoServerConfig ssoServerConfig = ssoServerTemplate.getServerConfig();
String client = req.getParam(paramName.client);
String ticket = req.getParamNotNull(paramName.ticket);
String sloCallback = req.getParam(paramName.ssoLogoutCall);
// 2校验提供的client是否为非法字符
if(SaSsoConsts.CLIENT_WILDCARD.equals(client)) {
return SaResult.error("无效 client 标识:" + client);
}
// 3校验签名
// if(ssoServerConfig.getIsCheckSign()) {
// ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.client, paramName.ticket, paramName.ssoLogoutCall);
// } else {
// SaSsoManager.printNoCheckSignWarningByRuntime();
// }
// 4校验ticket获取 loginId
Object loginId = ssoServerTemplate.checkTicket(ticket, client);
if(SaFoxUtil.isEmpty(loginId)) {
return SaResult.error("无效ticket" + ticket);
}
// 5注册此客户端的单点注销回调URL
ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback);
// 6 client 端响应结果
long remainSessionTimeout = ssoServerTemplate.getStpLogic().getSessionTimeoutByLoginId(loginId);
SaResult result = SaResult.data(loginId).set(paramName.remainSessionTimeout, remainSessionTimeout);
result = ssoServerConfig.checkTicketAppendData.apply(loginId, result);
return result;
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.sso.message.handle.server;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
import cn.dev33.satoken.sso.template.SaSsoTemplate;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.util.SaResult;
/**
* Sa-Token SSO 消息 处理器 - sso-server 处理 单点注销 的请求
*
* @author click33
* @since 1.43.0
*/
public class SaSsoMessageSignoutHandle implements SaSsoMessageHandle {
/**
* 获取所要处理的消息类型
*
* @return /
*/
public String getHandlerType() {
return SaSsoConsts.MESSAGE_SIGNOUT;
}
/**
* 执行方法
*
* @param ssoTemplate /
* @param message /
* @return /
*/
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate;
ParamName paramName = ssoServerTemplate.paramName;
if( ! ssoServerTemplate.getServerConfig().getIsSlo()) {
return SaResult.error("当前 sso-server 端未开启单点注销功能");
}
// 获取参数
SaRequest req = SaHolder.getRequest();
String loginId = req.getParam(paramName.loginId);
// step.2 单点注销
ssoServerTemplate.ssoLogout(loginId);
// 响应
return SaResult.ok();
}
}

View File

@ -32,6 +32,9 @@ public class ApiName {
/** SSO-Server端校验ticket 获取账号id */
public String ssoCheckTicket = "/sso/checkTicket";
/** SSO-Server端接受推送消息 */
public String ssoPushS = "/sso/pushS";
/** SSO-Server端获取userinfo */
public String ssoUserinfo = "/sso/userinfo";
@ -46,7 +49,10 @@ public class ApiName {
/** SSO-Client端单点注销的回调 */
public String ssoLogoutCall = "/sso/logoutCall";
/** SSO-Client端接受推送消息 */
public String ssoPushC = "/sso/pushC";
/**
* 批量修改path新增固定前缀
* @param prefix 示例值/sso-user/sso-admin
@ -56,10 +62,12 @@ public class ApiName {
this.ssoAuth = prefix + this.ssoAuth;
this.ssoDoLogin = prefix + this.ssoDoLogin;
this.ssoCheckTicket = prefix + this.ssoCheckTicket;
this.ssoPushS = prefix + this.ssoPushS;
this.ssoUserinfo = prefix + this.ssoUserinfo;
this.ssoSignout = prefix + this.ssoSignout;
this.ssoLogin = prefix + this.ssoLogin;
this.ssoLogout = prefix + this.ssoLogout;
this.ssoPushC = prefix + this.ssoPushC;
this.ssoLogoutCall = prefix + this.ssoLogoutCall;
return this;
}
@ -74,21 +82,30 @@ public class ApiName {
this.ssoAuth = this.ssoAuth.replaceFirst(oldPrefix, prefix);
this.ssoDoLogin = this.ssoDoLogin.replaceFirst(oldPrefix, prefix);
this.ssoCheckTicket = this.ssoCheckTicket.replaceFirst(oldPrefix, prefix);
this.ssoPushS = this.ssoPushS.replaceFirst(oldPrefix, prefix);
this.ssoUserinfo = this.ssoUserinfo.replaceFirst(oldPrefix, prefix);
this.ssoSignout = this.ssoSignout.replaceFirst(oldPrefix, prefix);
this.ssoLogin = this.ssoLogin.replaceFirst(oldPrefix, prefix);
this.ssoLogout = this.ssoLogout.replaceFirst(oldPrefix, prefix);
this.ssoPushC = this.ssoPushC.replaceFirst(oldPrefix, prefix);
this.ssoLogoutCall = this.ssoLogoutCall.replaceFirst(oldPrefix, prefix);
return this;
}
@Override
public String toString() {
return "ApiName [ssoAuth=" + ssoAuth + ", ssoDoLogin=" + ssoDoLogin + ", ssoCheckTicket=" + ssoCheckTicket
+ ", ssoUserinfo=" + ssoUserinfo + ", ssoSignout=" + ssoSignout + ", ssoLogin=" + ssoLogin
+ ", ssoLogout=" + ssoLogout + ", ssoLogoutCall=" + ssoLogoutCall + "]";
return "ApiName{" +
"ssoAuth='" + ssoAuth + '\'' +
", ssoDoLogin='" + ssoDoLogin + '\'' +
", ssoCheckTicket='" + ssoCheckTicket + '\'' +
", ssoPushS='" + ssoPushS + '\'' +
", ssoUserinfo='" + ssoUserinfo + '\'' +
", ssoSignout='" + ssoSignout + '\'' +
", ssoLogin='" + ssoLogin + '\'' +
", ssoLogout='" + ssoLogout + '\'' +
", ssoLogoutCall='" + ssoLogoutCall + '\'' +
", ssoPushC='" + ssoPushC + '\'' +
'}';
}
}

View File

@ -22,6 +22,7 @@ import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.model.SaCheckTicketResult;
import cn.dev33.satoken.sso.name.ApiName;
import cn.dev33.satoken.sso.name.ParamName;
@ -31,6 +32,8 @@ import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/**
* SSO 请求处理器 Client端
*
@ -74,8 +77,13 @@ public class SaSsoClientProcessor {
return ssoLogout();
}
// ---------- SSO-Client端接收消息推送
if(req.isPath(apiName.ssoPushC)) {
return ssoPushC();
}
// ---------- SSO-Client端单点注销的回调 [模式三]
if(req.isPath(apiName.ssoLogoutCall) && cfg.getIsSlo() && cfg.getIsHttp()) {
if(req.isPath(apiName.ssoLogoutCall) && cfg.getRegLogoutCall()) {
return ssoLogoutCall();
}
@ -154,6 +162,27 @@ public class SaSsoClientProcessor {
return SaSsoConsts.NOT_HANDLE;
}
/**
* SSO-Client端接收推送消息
*
* @return 处理结果
*/
public Object ssoPushC() {
SaSsoClientConfig ssoClientConfig = ssoClientTemplate.getClientConfig();
// 1校验签名
Map<String, String> paramMap = SaHolder.getRequest().getParamMap();
if(ssoClientConfig.getIsCheckSign()) {
ssoClientTemplate.getSignTemplate(ssoClientConfig.getClient()).checkParamMap(paramMap);
} else {
SaSsoManager.printNoCheckSignWarningByRuntime();
}
// 2处理消息
SaSsoMessage message = new SaSsoMessage(paramMap);
return ssoClientTemplate.handleMessage(message);
}
/**
* SSO-Client端单点注销 [模式二]
* @return 处理结果
@ -189,8 +218,8 @@ public class SaSsoClientProcessor {
}
// 调用 sso-server 认证中心单点注销API
String url = ssoClientTemplate.buildSloUrl(stpLogic.getLoginId());
SaResult result = ssoClientTemplate.request(url);
SaSsoMessage message = ssoClientTemplate.buildSloMessage(stpLogic.getLoginId());
SaResult result = ssoClientTemplate.pushMessageAsSaResult(message);
// 校验响应状态码
if(result.getCode() != null && SaResult.CODE_SUCCESS == result.getCode()) {
@ -224,8 +253,7 @@ public class SaSsoClientProcessor {
// 校验参数签名
if(ssoConfig.getIsCheckSign()) {
ssoClientTemplate.getSignTemplate(ssoConfig.getClient()).
checkRequest(req, paramName.loginId, paramName.client, paramName.autoLogout);
ssoClientTemplate.getSignTemplate(ssoConfig.getClient()).checkRequest(req, paramName.loginId, paramName.client, paramName.autoLogout);
} else {
SaSsoManager.printNoCheckSignWarningByRuntime();
}
@ -256,7 +284,7 @@ public class SaSsoClientProcessor {
// 计算当前 sso-client 的单点注销回调地址
String ssoLogoutCall = null;
if(cfg.getIsSlo()) {
if(cfg.getRegLogoutCall()) {
// 如果配置了回调地址就使用配置的值
if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) {
ssoLogoutCall = cfg.getCurrSsoLogoutCall();
@ -270,11 +298,9 @@ public class SaSsoClientProcessor {
}
}
// 构建请求URL
String checkUrl = ssoClientTemplate.buildCheckTicketUrl(ticket, ssoLogoutCall);
// 发起请求
SaResult result = ssoClientTemplate.request(checkUrl);
SaSsoMessage message = ssoClientTemplate.buildCheckTicketMessage(ticket, ssoLogoutCall);
SaResult result = ssoClientTemplate.pushMessageAsSaResult(message);
// 校验
if(result.getCode() != null && result.getCode() == SaResult.CODE_SUCCESS) {

View File

@ -22,6 +22,7 @@ import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.name.ApiName;
import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
@ -30,6 +31,8 @@ import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/**
* SSO 请求处理器 Server端
*
@ -63,26 +66,26 @@ public class SaSsoServerProcessor {
// ------------------ 路由分发 ------------------
// ---------- SSO-Server端授权地址
// sso-server授权地址
if(req.isPath(apiName.ssoAuth)) {
return ssoAuth();
}
// ---------- SSO-Server端RestAPI 登录接口
// sso-serverRestAPI 登录接口
if(req.isPath(apiName.ssoDoLogin)) {
return ssoDoLogin();
}
// ---------- SSO-Server端校验ticket 获取账号id
if(req.isPath(apiName.ssoCheckTicket) && cfg.getIsHttp()) {
return ssoCheckTicket();
}
// ---------- SSO-Server端单点注销
// sso-server单点注销
if(req.isPath(apiName.ssoSignout)) {
return ssoSignout();
}
// sso-server接收推送消息
if(req.isPath(apiName.ssoPushS)) {
return ssoPush();
}
// 默认返回
return SaSsoConsts.NOT_HANDLE;
}
@ -162,78 +165,11 @@ public class SaSsoServerProcessor {
return cfg.doLoginHandle.apply(req.getParam(paramName.name), req.getParam(paramName.pwd));
}
/**
* SSO-Server端校验ticket 获取账号id [模式三]
* @return 处理结果
*/
public Object ssoCheckTicket() {
ParamName paramName = ssoServerTemplate.paramName;
// 1获取参数
SaRequest req = SaHolder.getRequest();
SaSsoServerConfig ssoServerConfig = ssoServerTemplate.getServerConfig();
String client = req.getParam(paramName.client);
String ticket = req.getParamNotNull(paramName.ticket);
String sloCallback = req.getParam(paramName.ssoLogoutCall);
// 2校验提供的client是否为非法字符
if(SaSsoConsts.CLIENT_WILDCARD.equals(client)) {
return SaResult.error("无效 client 标识:" + client);
}
// 3校验签名
if(ssoServerConfig.getIsCheckSign()) {
ssoServerTemplate.getSignTemplate(client).checkRequest(req,
paramName.client, paramName.ticket, paramName.ssoLogoutCall);
} else {
SaSsoManager.printNoCheckSignWarningByRuntime();
}
// 4校验ticket获取 loginId
Object loginId = ssoServerTemplate.checkTicket(ticket, client);
if(SaFoxUtil.isEmpty(loginId)) {
return SaResult.error("无效ticket" + ticket);
}
// 5注册此客户端的单点注销回调URL
ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback);
// 6 client 端响应结果
long remainSessionTimeout = ssoServerTemplate.getStpLogic().getSessionTimeoutByLoginId(loginId);
SaResult result = SaResult.data(loginId).set(paramName.remainSessionTimeout, remainSessionTimeout);
result = ssoServerConfig.checkTicketAppendData.apply(loginId, result);
return result;
}
/**
* SSO-Server端单点注销
* @return 处理结果
*/
public Object ssoSignout() {
// 获取对象
SaRequest req = SaHolder.getRequest();
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
ParamName paramName = ssoServerTemplate.paramName;
// SSO-Server端单点注销 [用户访问式] (不带loginId参数)
if(cfg.getIsSlo() && ! req.hasParam(paramName.loginId)) {
return ssoSignoutByUserVisit();
}
// SSO-Server端单点注销 [Client调用式] (带loginId参数)
if(cfg.getIsSlo() && req.hasParam(paramName.loginId)) {
return ssoSignoutByClientHttp();
}
// 默认返回
return SaSsoConsts.NOT_HANDLE;
}
/**
* SSO-Server端单点注销 [用户访问式]
* @return 处理结果
*/
public Object ssoSignoutByUserVisit() {
// 获取对象
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
@ -245,43 +181,41 @@ public class SaSsoServerProcessor {
}
// 完成
return ssoLogoutBack(req, res);
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoServerTemplate.paramName);
}
/**
* SSO-Server端单点注销 [Client调用式]
* SSO-Server端接收推送消息
*
* @return 处理结果
*/
public Object ssoSignoutByClientHttp() {
public Object ssoPush() {
ParamName paramName = ssoServerTemplate.paramName;
SaSsoServerConfig ssoServerConfig = ssoServerTemplate.getServerConfig();
// 获取参数
// 1获取参数
SaRequest req = SaHolder.getRequest();
String loginId = req.getParam(paramName.loginId);
String client = req.getParam(paramName.client);
// step.1 校验签名
if(ssoServerTemplate.getServerConfig().getIsCheckSign()) {
ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.client, paramName.loginId);
// 2校验提供的client是否为非法字符
if(SaSsoConsts.CLIENT_WILDCARD.equals(client)) {
return SaResult.error("无效 client 标识:" + client);
}
// 3校验签名
Map<String, String> paramMap = req.getParamMap();
if(ssoServerConfig.getIsCheckSign()) {
ssoServerTemplate.getSignTemplate(client).checkParamMap(paramMap);
} else {
SaSsoManager.printNoCheckSignWarningByRuntime();
}
// step.2 单点注销
ssoServerTemplate.ssoLogout(loginId);
// 响应
return SaResult.ok();
// 处理消息
SaSsoMessage message = new SaSsoMessage(paramMap);
if( ! ssoServerTemplate.messageHolder.hasHandle(message.getType())) {
return SaResult.error("未能找到消息处理器: " + message.getType());
}
return ssoServerTemplate.handleMessage(message);
}
/**
* 封装单点注销成功后返回结果
* @param req SaRequest对象
* @param res SaResponse对象
* @return 返回结果
*/
public Object ssoLogoutBack(SaRequest req, SaResponse res) {
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoServerTemplate.paramName);
}
}

View File

@ -22,11 +22,13 @@ import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.client.SaSsoMessageLogoutCallHandle;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
import java.util.TreeMap;
/**
* Sa-Token SSO 模板方法类 Client端
@ -36,12 +38,8 @@ import java.util.TreeMap;
*/
public class SaSsoClientTemplate extends SaSsoTemplate {
/**
* 获取底层使用的SsoClient配置对象
* @return /
*/
public SaSsoClientConfig getClientConfig() {
return SaSsoManager.getClientConfig();
public SaSsoClientTemplate() {
super.messageHolder.addHandle(new SaSsoMessageLogoutCallHandle());
}
@ -66,7 +64,7 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
*/
public Object getData(String path, Map<String, Object> paramMap) {
String url = buildCustomPathUrl(path, paramMap);
return getClientConfig().sendHttp.apply(url);
return request(url);
}
// ---------------------- 构建URL ----------------------
@ -108,56 +106,6 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl);
}
/**
* 构建URL校验ticket的URL
* <p> 在模式三下Client端拿到Ticket后根据此地址向Server端发送请求获取账号id
* @param ticket ticket码
* @param ssoLogoutCallUrl 单点注销时的回调URL
* @return 构建完毕的URL
*/
public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
SaSsoClientConfig ssoConfig = getClientConfig();
// 1url
String url = ssoConfig.splicingCheckTicketUrl();
// 2参数clientticketssoLogoutCall
Map<String, Object> paramMap = new TreeMap<>();
paramMap.put(paramName.ticket, ticket);
paramMap.put(paramName.client, ssoConfig.getClient());
paramMap.put(paramName.ssoLogoutCall, ssoLogoutCallUrl);
// 追加签名参数并序列化为kv字符串
String signParamStr = getSignTemplate(ssoConfig.getClient()).addSignParamsAndJoin(paramMap);
// 3拼接
return SaFoxUtil.joinParam(url, signParamStr);
}
/**
* 构建URL单点注销URL
* @param loginId 要注销的账号id
* @return 单点注销URL
*/
public String buildSloUrl(Object loginId) {
// 获取所需对象
SaSsoClientConfig ssoConfig = getClientConfig();
String url = ssoConfig.splicingSloUrl();
String currClient = ssoConfig.getClient();
// 组织请求参数
Map<String, Object> paramMap = new TreeMap<>();
paramMap.put(paramName.loginId, loginId);
paramMap.put(paramName.client, currClient);
// 追加签名参数并序列化为kv字符串
String signParamsStr = getSignTemplate(currClient).addSignParamsAndJoin(paramMap);
// 拼接到 url
return SaFoxUtil.joinParam(url, signParamsStr);
}
/**
* 构建URLServer端 getData 地址带签名等参数
* @param paramMap 查询参数
@ -181,7 +129,7 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
String url = path;
if( ! url.startsWith("http") ) {
String serverUrl = ssoConfig.getServerUrl();
SaSsoException.notEmpty(serverUrl, "请先配置 sa-token.sso.server-url 地址", SaSsoErrorCode.CODE_30012);
SaSsoException.notEmpty(serverUrl, "请先配置 sa-token.sso-client.server-url 地址", SaSsoErrorCode.CODE_30012);
url = SaFoxUtil.spliceTwoUrl(serverUrl, path);
}
@ -193,6 +141,86 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return SaFoxUtil.joinParam(url, signParamsStr);
}
/**
* 构建消息校验 ticket
*
* @param ticket ticket码
* @param ssoLogoutCallUrl 单点注销时的回调URL
* @return 构建完毕的URL
*/
public SaSsoMessage buildCheckTicketMessage(String ticket, String ssoLogoutCallUrl) {
SaSsoClientConfig ssoConfig = getClientConfig();
SaSsoMessage message = new SaSsoMessage();
message.setType(SaSsoConsts.MESSAGE_CHECK_TICKET);
message.set(paramName.client, ssoConfig.getClient());
message.set(paramName.ticket, ticket);
message.set(paramName.ssoLogoutCall, ssoLogoutCallUrl);
return message;
}
/**
* 构建消息单点注销
* @param loginId 要注销的账号 id
* @return 单点注销URL
*/
public SaSsoMessage buildSloMessage(Object loginId) {
SaSsoClientConfig ssoConfig = getClientConfig();
SaSsoMessage message = new SaSsoMessage();
message.setType(SaSsoConsts.MESSAGE_SIGNOUT);
message.set(paramName.client, ssoConfig.getClient());
message.set(paramName.loginId, loginId);
return message;
}
// ------------------- 消息推送 -------------------
/**
* sso-server 推送消息
*
* @param message /
* @return /
*/
public String pushMessage(SaSsoMessage message) {
SaSsoClientConfig ssoConfig = getClientConfig();
// 拼接 push-url 地址
String pushUrl = ssoConfig.splicingPushUrl();
SaSsoException.notTrue(! SaFoxUtil.isUrl(pushUrl), "无效 push-url 地址:" + pushUrl, SaSsoErrorCode.CODE_30023);
// 组织参数
message.set(paramName.client, ssoConfig.getClient());
message.checkType();
String paramsStr = getSignTemplate(ssoConfig.getClient()).addSignParamsAndJoin(message);
// 发起请求
String finalUrl = SaFoxUtil.joinParam(pushUrl, paramsStr);
return request(finalUrl);
}
/**
* sso-server 推送消息并将返回值转为 SaResult
*
* @param message /
* @return /
*/
public SaResult pushMessageAsSaResult(SaSsoMessage message) {
String res = pushMessage(message);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(res);
return new SaResult(map);
}
// ------------------- Bean 对象获取 -------------------
/**
* 获取底层使用的SsoClient配置对象
* @return /
*/
public SaSsoClientConfig getClientConfig() {
return SaSsoManager.getClientConfig();
}
/**
* 获取底层使用的 API 签名对象
* @param client 指定客户端标识 null 代表获取默认的
@ -212,18 +240,4 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return new SaSignTemplate(signConfig);
}
// ------------------- 发起请求 -------------------
/**
* 发出请求并返回 SaResult 结果
* @param url 请求地址
* @return 返回的结果
*/
public SaResult request(String url) {
String body = getClientConfig().sendHttp.apply(url);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
return new SaResult(map);
}
}

View File

@ -17,7 +17,6 @@ package cn.dev33.satoken.sso.template;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaSignConfig;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.sso.SaSsoManager;
@ -25,10 +24,14 @@ import cn.dev33.satoken.sso.config.SaSsoClientModel;
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.server.SaSsoMessageCheckTicketHandle;
import cn.dev33.satoken.sso.message.handle.server.SaSsoMessageSignoutHandle;
import cn.dev33.satoken.sso.model.SaSsoClientInfo;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.*;
@ -40,12 +43,9 @@ import java.util.*;
*/
public class SaSsoServerTemplate extends SaSsoTemplate {
/**
* 获取底层使用的SsoServer配置对象
* @return /
*/
public SaSsoServerConfig getServerConfig() {
return SaSsoManager.getServerConfig();
public SaSsoServerTemplate() {
super.messageHolder.addHandle(new SaSsoMessageCheckTicketHandle());
super.messageHolder.addHandle(new SaSsoMessageSignoutHandle());
}
// ---------------------- Ticket 操作 ----------------------
@ -165,7 +165,6 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
}
//
/**
* 根据 账号id 创建一个 Ticket码
* @param loginId 账号id
@ -217,8 +216,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
} else {
// 开始详细比对
if(SaFoxUtil.notEquals(client, ticketClient)) {
throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket)
.setCode(SaSsoErrorCode.CODE_30011);
throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket).setCode(SaSsoErrorCode.CODE_30011);
}
}
@ -241,19 +239,28 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
return SaFoxUtil.getRandomString(64);
}
// ---------------------- Client 信息获取 ----------------------
/**
* 获取所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
* 获取所有 Client
*
* @return /
*/
public List<SaSsoClientModel> getClients() {
Map<String, SaSsoClientModel> clients = getServerConfig().getClients();
return new ArrayList<>(clients.values());
}
/**
* 获取应用信息无效 client 返回 null
*
* @param client /
* @return /
*/
// public String getAllowUrl(String client) {
// if(SaFoxUtil.isEmpty(client)) {
// return getServerConfig().getAllowUrl();
// } else {
// return getClientNotNull(client).getAllowUrl();
// }
// }
public SaSsoClientModel getClient(String client) {
return getServerConfig().getClients().get(client);
}
/**
* 获取应用信息无效 client 则抛出异常
@ -295,13 +302,70 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
}
/**
* 获取应用信息无效 client 返回 null
* 获取所有需要接收消息推送的 Client
*
* @param client /
* @return /
*/
public SaSsoClientModel getClient(String client) {
return getServerConfig().getClients().get(client);
public List<SaSsoClientModel> getNeedPushClients() {
List<SaSsoClientModel> list = new ArrayList<>();
List<SaSsoClientModel> clients = getClients();
for(SaSsoClientModel scm : clients) {
if (scm.isValidNoticeUrl()) {
list.add(scm);
}
}
return list;
}
// ------------------- 重定向 URL 构建与校验 -------------------
/**
* 构建URLServer端向Client下放ticket的地址
* @param loginId 账号id
* @param client 客户端标识
* @param redirect Client端提供的重定向地址
* @return see note
*/
public String buildRedirectUrl(Object loginId, String client, String redirect) {
// 校验 重定向地址 是否合法
checkRedirectUrl(client, redirect);
// 删掉 旧Ticket
deleteTicket(getTicketValue(loginId));
// 创建 新Ticket
String ticket = createTicket(loginId, client);
// 构建 授权重定向地址 Server端 根据此地址向 Client端 下放Ticket
return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket);
}
/**
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
* @param url url
* @return 编码过后的url
*/
public String encodeBackParam(String url) {
// 获取back参数所在位置
int index = url.indexOf("?" + paramName.back + "=");
if(index == -1) {
index = url.indexOf("&" + paramName.back + "=");
if(index == -1) {
return url;
}
}
// 开始编码
int length = paramName.back.length() + 2;
String back = url.substring(index + length);
back = SaFoxUtil.encodeUrl(back);
// 放回url中
url = url.substring(0, index + length) + back;
return url;
}
/**
@ -402,55 +466,11 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
}
}
// ------------------- SSO -------------------
// ------------------- 单点注销 -------------------
/**
* 指定账号单点注销
* @param loginId 指定账号
*/
public void ssoLogout(Object loginId) {
// 如果这个账号尚未登录则无操作
SaSession session = getStpLogic().getSessionByLoginId(loginId, false);
if(session == null) {
return;
}
// step.1 遍历通知 Client 端注销会话
List<SaSsoClientInfo> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
scmList.forEach(scm -> {
notifyClientLogout(loginId, scm, false);
});
// step.2 Server端注销
getStpLogic().logout(loginId);
}
/**
* 计算下一个 index
* @param scmList /
* @return /
*/
public int calcNextIndex(List<SaSsoClientInfo> scmList) {
// 如果目前还没有任何登录记录则直接返回0
if(scmList == null || scmList.isEmpty()) {
return 0;
}
// 获取目前最大的index值
int maxIndex = scmList.get(scmList.size() - 1).index;
// 如果已经是 int 最大值了则直接返回0
if(maxIndex == Integer.MAX_VALUE) {
return 0;
}
// 否则返回最大值+1
maxIndex++;
return maxIndex;
}
/**
* 为指定账号id注册单点注销回调信息模式三
* 为指定账号 id 注册单点注销回调信息模式三
* @param loginId 账号id
* @param client 指定客户端标识可为null
* @param sloCallbackUrl 单点注销时的回调URL
@ -487,6 +507,52 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
session.set(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, scmList);
}
/**
* 计算下一个 index
* @param scmList /
* @return /
*/
public int calcNextIndex(List<SaSsoClientInfo> scmList) {
// 如果目前还没有任何登录记录则直接返回0
if(scmList == null || scmList.isEmpty()) {
return 0;
}
// 获取目前最大的index值
int maxIndex = scmList.get(scmList.size() - 1).index;
// 如果已经是 int 最大值了则直接返回0
if(maxIndex == Integer.MAX_VALUE) {
return 0;
}
// 否则返回最大值+1
maxIndex++;
return maxIndex;
}
/**
* 指定账号单点注销
* @param loginId 指定账号
*/
public void ssoLogout(Object loginId) {
// 1消息推送单点注销
pushToAllClientByLogoutCall(loginId);
// 2SaSession 挂载的 Client 端注销会话
SaSession session = getStpLogic().getSessionByLoginId(loginId, false);
if(session == null) {
return;
}
List<SaSsoClientInfo> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
scmList.forEach(scm -> {
notifyClientLogout(loginId, scm, false);
});
// 3Server 端本身注销
getStpLogic().logout(loginId);
}
/**
* 通知指定账号的指定客户端注销
* @param loginId 指定账号
@ -500,7 +566,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
return;
}
// url
// 如果此 Client 并没有注册 单点登录 回调地址则立即返回
String sloCallUrl = scm.getSloCallbackUrl();
if(SaFoxUtil.isEmpty(sloCallUrl)) {
return;
@ -517,74 +583,102 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr);
// 发起请求
getServerConfig().sendHttp.apply(finalUrl);
request(finalUrl);
}
// ---------------------- 构建URL ----------------------
// ------------------- 消息推送 -------------------
/**
* 构建URLServer端向Client下放ticket的地址
* @param loginId 账号id
* @param client 客户端标识
* @param redirect Client端提供的重定向地址
* @return see note
* 向指定 Client 推送消息
* @param clientModel /
* @param message /
* @return /
*/
public String buildRedirectUrl(Object loginId, String client, String redirect) {
// 校验 重定向地址 是否合法
checkRedirectUrl(client, redirect);
// 删掉 旧Ticket
deleteTicket(getTicketValue(loginId));
// 创建 新Ticket
String ticket = createTicket(loginId, client);
// 构建 授权重定向地址 Server端 根据此地址向 Client端 下放Ticket
return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket);
public String pushMessage(SaSsoClientModel clientModel, SaSsoMessage message) {
message.checkType();
String noticeUrl = clientModel.splicingNoticeUrl();
String paramsStr = getSignTemplate(clientModel.getClient()).addSignParamsAndJoin(message);
String finalUrl = SaFoxUtil.joinParam(noticeUrl, paramsStr);
return request(finalUrl);
}
/**
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
* @param url url
* @return 编码过后的url
* 向指定 client 推送消息并将返回值转为 SaResult
*
* @param clientModel /
* @param message /
* @return /
*/
public String encodeBackParam(String url) {
public SaResult pushMessageAsSaResult(SaSsoClientModel clientModel, SaSsoMessage message) {
String res = pushMessage(clientModel, message);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(res);
return new SaResult(map);
}
// 获取back参数所在位置
int index = url.indexOf("?" + paramName.back + "=");
if(index == -1) {
index = url.indexOf("&" + paramName.back + "=");
if(index == -1) {
return url;
/**
* 向指定 Client 推送消息
* @param client /
* @param message /
* @return /
*/
public String pushMessage(String client, SaSsoMessage message) {
return pushMessage(getClientNotNull(client), message);
}
/**
* 向指定 client 推送消息并将返回值转为 SaResult
*
* @param client /
* @param message /
* @return /
*/
public SaResult pushMessageAsSaResult(String client, SaSsoMessage message) {
String res = pushMessage(client, message);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(res);
return new SaResult(map);
}
/**
* 向所有 Client 推送消息
*
* @param message /
*/
public void pushToAllClient(SaSsoMessage message) {
List<SaSsoClientModel> mode3Clients = getNeedPushClients();
for (SaSsoClientModel client : mode3Clients) {
pushMessage(client, message);
}
}
/**
* 向所有 Client 推送消息单点注销回调
*
* @param loginId /
*/
public void pushToAllClientByLogoutCall(Object loginId) {
List<SaSsoClientModel> npClients = getNeedPushClients();
for (SaSsoClientModel client : npClients) {
if(client.getIsSlo()) {
SaSsoMessage message = new SaSsoMessage();
message.setType(SaSsoConsts.MESSAGE_LOGOUT_CALL);
message.set(paramName.loginId, loginId);
pushMessage(client, message);
}
}
// 开始编码
int length = paramName.back.length() + 2;
String back = url.substring(index + length);
back = SaFoxUtil.encodeUrl(back);
// 放回url中
url = url.substring(0, index + length) + back;
return url;
}
// ---------------------- 重构 ----------------------
// ------------------- Bean 获取 -------------------
/**
* 校验 Ticket 获取账号id如果此ticket是有效的则立即删除
* @param ticket Ticket码
* @param client client 标识
* 获取底层使用的SsoServer配置对象
* @return /
*/
public void checkTicketVerifySign(SaRequest req, String ticket, String client) {
SaSignTemplate signTemplate = getSignTemplate(client);
signTemplate.checkRequest(req, paramName.client, paramName.ticket, paramName.ssoLogoutCall);
public SaSsoServerConfig getServerConfig() {
return SaSsoManager.getServerConfig();
}
/**
* 获取底层使用的 API 签名对象
* @param client 指定客户端标识 null 代表获取默认的
@ -609,8 +703,6 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
}
// ------------------- 返回相应key -------------------
/**

View File

@ -17,10 +17,15 @@ package cn.dev33.satoken.sso.template;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.SaSsoMessageHolder;
import cn.dev33.satoken.sso.name.ApiName;
import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/**
* Sa-Token SSO 模板方法类 公共端
@ -78,4 +83,40 @@ public class SaSsoTemplate {
return SaManager.getSaSignTemplate();
}
// ----------- 消息处理
public SaSsoMessageHolder messageHolder = new SaSsoMessageHolder();
/**
* 发送 Http 请求
*
* @param url /
* @return /
*/
public String request(String url) {
return SaManager.getSaHttpTemplate().get(url);
}
/**
* 发送 Http 请求并将响应结果转换为 SaResult
*
* @param url 请求地址
* @return 返回的结果
*/
public SaResult requestAsSaResult(String url) {
String body = request(url);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
return new SaResult(map);
}
/**
* 处理指定消息
*
* @param message /
*/
public Object handleMessage(SaSsoMessage message) {
return messageHolder.handleMessage(this, message);
}
}

View File

@ -95,16 +95,6 @@ public class SaSsoUtil {
return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, client);
}
// /**
// * 获取所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
// *
// * @param client 应用标识
// * @return /
// */
// public static String getAllowUrl(String client) {
// return SaSsoServerProcessor.instance.ssoServerTemplate.getAllowUrl(client);
// }
/**
* 校验重定向url合法性
*
@ -117,16 +107,6 @@ public class SaSsoUtil {
// ------------------- SSO 模式三 -------------------
/**
* 构建URL校验ticket的URL
* @param ticket ticket码
* @param ssoLogoutCallUrl 单点注销时的回调URL
* @return 构建完毕的URL
*/
public static String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
return SaSsoClientProcessor.instance.ssoClientTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl);
}
/**
* 为指定账号id注册单点注销回调URL
@ -138,15 +118,6 @@ public class SaSsoUtil {
SaSsoServerProcessor.instance.ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallbackUrl);
}
/**
* 构建URL单点注销URL
* @param loginId 要注销的账号id
* @return 单点注销URL
*/
public static String buildSloUrl(Object loginId) {
return SaSsoClientProcessor.instance.ssoClientTemplate.buildSloUrl(loginId);
}
/**
* 指定账号单点注销 (以Server方发起)
* @param loginId 指定账号

View File

@ -55,6 +55,18 @@ public class SaSsoConsts {
/** SSO 模式3 */
public static final int SSO_MODE_3 = 3;
// /** 消息类型:单点注销 */
// public static final String MESSAGE_LOGOUT = "logout";
/** 消息类型:校验 ticket */
public static final String MESSAGE_CHECK_TICKET = "checkTicket";
/** 消息类型:单点注销 */
public static final String MESSAGE_SIGNOUT = "signout";
/** 消息类型:单点注销回调 */
public static final String MESSAGE_LOGOUT_CALL = "logoutCall";
}