refactor: 调整 SSO 相关示例

This commit is contained in:
click33 2025-05-08 20:50:30 +08:00
parent 2ecd52b3be
commit 88f99c49fb
25 changed files with 117 additions and 57 deletions

View File

@ -58,8 +58,9 @@ cd sa-token-demo-sso-server & call mvn clean & cd ..
cd sa-token-demo-sso1-client & call mvn clean & cd ..
cd sa-token-demo-sso2-client & call mvn clean & cd ..
cd sa-token-demo-sso3-client & call mvn clean & cd ..
cd sa-token-demo-sso3-client-test2 & call mvn clean & cd ..
cd sa-token-demo-sso3-client-nosdk & call mvn clean & cd ..
cd sa-token-demo-sso3-client-resdk & call mvn clean & cd ..
cd sa-token-demo-sso3-client-anon & call mvn clean & cd ..
cd ..
cd sa-token-demo-sso-for-solon

View File

@ -41,10 +41,10 @@
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 插件:整合redis (使用jackson序列化方式) -->
<!-- Sa-Token 插件:整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<dependency>
@ -58,7 +58,7 @@
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Sa-Token 插件:整合 Forest 请求工具 -->
<!-- Sa-Token 插件:整合 Forest 请求工具 (模式三需要通过 http 请求推送消息) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-forest</artifactId>

View File

@ -12,20 +12,20 @@ import org.springframework.web.bind.annotation.RestController;
public class HomeController {
// 平台化首页
@RequestMapping("/home")
@RequestMapping({"/", "/home"})
public Object index() {
// 如果未登录则先去登录
if(!StpUtil.isLogin()) {
return SaHolder.getResponse().redirect("/sso/auth");
}
// 拼接各个子系统的地址格式形如/sso/auth?redirect=${子系统首页}/sso/login?back=${子系统首页}
String link1 = "/sso/auth?redirect=http://sa-sso-client1.com:9003/sso/login?back=http://sa-sso-client1.com:9003/";
String link2 = "/sso/auth?redirect=http://sa-sso-client2.com:9003/sso/login?back=http://sa-sso-client2.com:9003/";
String link3 = "/sso/auth?redirect=http://sa-sso-client3.com:9003/sso/login?back=http://sa-sso-client3.com:9003/";
// 拼接各个子系统的地址格式形如/sso/auth?client=xxx&redirect=${子系统首页}/sso/login?back=${子系统首页}
String link1 = "/sso/auth?client=sso-client3&redirect=http://sa-sso-client1.com:9003/sso/login?back=http://sa-sso-client1.com:9003/";
String link2 = "/sso/auth?client=sso-client3&redirect=http://sa-sso-client2.com:9003/sso/login?back=http://sa-sso-client2.com:9003/";
String link3 = "/sso/auth?client=sso-client3&redirect=http://sa-sso-client3.com:9003/sso/login?back=http://sa-sso-client3.com:9003/";
// 组织网页结构返回到前端
String title = "<h2>SSO 平台首页</h2>";
String title = "<h2>SSO 平台首页 (平台中心模式)</h2>";
String client1 = "<p><a href='" + link1 + "' target='_blank'> 进入Client1系统 </a></p>";
String client2 = "<p><a href='" + link2 + "' target='_blank'> 进入Client2系统 </a></p>";
String client3 = "<p><a href='" + link3 + "' target='_blank'> 进入Client3系统 </a></p>";

View File

@ -22,7 +22,7 @@ public class SsoServerController {
/**
* SSO-Server端处理所有SSO相关请求
* http://{host}:{port}/sso/auth -- 单点登录授权地址
* http://{host}:{port}/sso/auth -- 单点登录授权地址
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口接受参数namepwd
* http://{host}:{port}/sso/signout -- 单点注销地址isSlo=true时打开
*/
@ -42,19 +42,18 @@ public class SsoServerController {
// 配置登录处理函数
ssoServerTemplate.strategy.doLoginHandle = (name, pwd) -> {
// 此处仅做模拟登录真实环境应该查询数据进行登录
// 此处仅做模拟登录真实环境应该查询数据进行登录
if("sa".equals(name) && "123456".equals(pwd)) {
String deviceId = SaHolder.getRequest().getParam("deviceId", SaFoxUtil.getRandomString(32));
StpUtil.login(10001, SaLoginParameter.create().setDeviceId(deviceId));
StpUtil.login(10001, new SaLoginParameter().setDeviceId(deviceId));
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
}
return SaResult.error("登录失败!");
};
// 添加消息处理器userinfo (获取用户资料) 用于在模式三下 client 端开放拉取数据的接口
// 添加消息处理器userinfo (获取用户资料) 用于 client 端开放拉取数据的接口
ssoServerTemplate.messageHolder.addHandle("userinfo", (ssoTemplate, message) -> {
System.out.println("收到消息:" + message);
System.out.println("loginId=" + message.get("loginId"));
// 自定义返回结果模拟
return SaResult.ok()

View File

@ -7,7 +7,7 @@ sa-token:
# 打印操作日志
is-log: true
# ------- SSO 模式一配置 (非模式一不需要配置)
# SSO 模式一配置 (非模式一不需要配置)
# cookie:
# # 配置 Cookie 作用域
# domain: stp.com
@ -16,7 +16,7 @@ sa-token:
sso-server:
# Ticket有效期 (单位: 秒),默认五分钟
ticket-timeout: 300
# 主页路由,在不指定 redirect 参数时,默认跳转的地址
# 主页路由:在 /sso/auth 登录页不指定 redirect 参数时,默认跳转的地址
home-route: /home
# 是否启用匿名 client (开启匿名 client 后,允许客户端接入时不提交 client 参数)
allow-anon-client: true
@ -26,6 +26,10 @@ sa-token:
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# 应用列表:配置接入的应用信息
clients:
# 应用 sso-client1采用模式一对接 (同域、同Redis)
sso-client1:
client: sso-client1
allow-url: "*"
# 应用 sso-client2采用模式二对接 (跨域、同Redis)
sso-client2:
client: sso-client2

View File

@ -34,21 +34,19 @@
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 插件:整合SSO -->
<!-- Sa-Token 插件:整合 SSO -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>

View File

@ -1,12 +1,17 @@
package com.pj.sso;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* Sa-Token-SSO Client端 Controller
* @author click33
@ -16,13 +21,17 @@ public class SsoClientController {
// SSO-Client端首页
@RequestMapping("/")
public String index() {
String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl();
String signoutUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
public String index(HttpServletRequest request) {
String url = SaFoxUtil.encodeUrl( SaFoxUtil.joinParam(SaHolder.getRequest().getUrl(), request.getQueryString()) );
SaSsoClientConfig cfg = SaSsoManager.getClientConfig();
String str = "<h2>Sa-Token SSO-Client 应用端 (模式一)</h2>" +
"<p>当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")</p>" +
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
"<a href=\"javascript:location.href='" + signoutUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
"<p>" +
"<a href='" + cfg.splicingAuthUrl() + "?mode=simple&client=" + cfg.getClient() + "&redirect=" + url + "'>登录</a> - " +
"<a href='" + cfg.splicingSignoutUrl() + "?singleDeviceIdLogout=true&back=" + url + "'>单浏览器注销</a> - " +
"<a href='" + cfg.splicingSignoutUrl() + "?back=" + url + "'>全端注销</a> " +
"</p>";
return str;
}

View File

@ -6,10 +6,13 @@ server:
sa-token:
# SSO-相关配置
sso-client:
# client 标识
client: sso-client1
# SSO-Server端主机地址
server-url: http://sso.stp.com:9000
# 配置 Sa-Token 单独使用的Redis连接 (需要引入 sa-token-alone-redis 依赖) (此处需要和 SSO-Server 端连接同一个Redis
# 配置 Sa-Token 单独使用的Redis连接此处需要和 SSO-Server 端连接同一个 Redis
# 注:使用 alone-redis 需要在 pom.xml 引入 sa-token-alone-redis 依赖
alone-redis:
# Redis数据库索引
database: 1

View File

@ -41,10 +41,10 @@
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>

View File

@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class H5Controller {
// 当前是否登录
// 判断当前是否登录
@RequestMapping("/sso/isLogin")
public Object isLogin() {
return SaResult.data(StpUtil.isLogin()).set("loginId", StpUtil.getLoginIdDefaultNull());
@ -31,7 +31,7 @@ public class H5Controller {
return SaResult.data(serverAuthUrl);
}
// 根据ticket进行登录
// 根据 ticket 进行登录
@RequestMapping("/sso/doLoginByTicket")
public SaResult doLoginByTicket(String ticket) {
SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket);

View File

@ -4,6 +4,9 @@ server:
# sa-token配置
sa-token:
# 打印操作日志
is-log: true
# SSO-相关配置
sso-client:
# 应用标识
@ -17,7 +20,7 @@ sa-token:
# 配置 Sa-Token 单独使用的Redis连接此处需要和 SSO-Server 端连接同一个 Redis
# 注:使用 alone-redis 需要在 pom.xml 引入 sa-token-alone-redis 依赖
alone-redis:
alone-redis:
# Redis数据库索引
database: 1
# Redis服务器地址

View File

@ -40,13 +40,13 @@
<artifactId>sa-token-sso</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>${sa-token.version}</version>
</dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>

View File

@ -47,6 +47,8 @@ public class SsoClientController {
// 配置SSO相关参数
@Autowired
private void configSso(SaSsoClientTemplate ssoClientTemplate) {
// 重写 loginId centerId 转换策略函数做到本地应用 userId 与认证中心 userId 的互相映射
// // centerId 转换为 loginId 的函数
// ssoClientTemplate.strategy.convertCenterIdToLoginId = (centerId) -> {
// return "Stu" + centerId;
@ -72,7 +74,10 @@ public class SsoClientController {
return "尚未登录,无法获取";
}
// 获取本地 loginId 对应的认证中心 centerId
// 原写法直接调用 StpUtil.getLoginId() 当做 centerId 来提交
// Object centerId = StpUtil.getLoginId();
// 新写法获取本地 loginId 对应的认证中心 centerId
Object centerId = SaSsoClientUtil.getSsoTemplate().strategy.convertLoginIdToCenterId.run(StpUtil.getLoginId());
// 推送消息

View File

@ -21,7 +21,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Http 请求工具 -->
<dependency>
<groupId>com.dtflys.forest</groupId>

View File

@ -41,10 +41,10 @@
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>

View File

@ -48,7 +48,7 @@ public class SaSsoServerConfig implements Serializable {
public long ticketTimeout = 60 * 5;
/**
* 主页路由 /sso/auth 登录后不指定 redirect 参数的情况下默认跳转的路由
* 主页路由 /sso/auth 登录页不指定 redirect 参数时默认跳转的地址
*/
public String homeRoute;
@ -176,14 +176,14 @@ public class SaSsoServerConfig implements Serializable {
}
/**
* @return 主页路由 /sso/auth 登录后不指定 redirect 参数的情况下默认跳转的路由
* @return 主页路由 /sso/auth 登录页不指定 redirect 参数时默认跳转的地址
*/
public String getHomeRoute() {
return homeRoute;
}
/**
* @param homeRoute 主页路由 /sso/auth 登录后不指定 redirect 参数的情况下默认跳转的路由
* @param homeRoute 主页路由 /sso/auth 登录页不指定 redirect 参数时默认跳转的地址
* @return 对象自身
*/
public SaSsoServerConfig setHomeRoute(String homeRoute) {

View File

@ -41,7 +41,7 @@ public class SaSsoMessage extends LinkedHashMap<String, Object> implements SaSet
/**
* KEYTYPE
*/
public static final String MSG_TYPE = "msg_type";
public static final String MSG_TYPE = "msgType";
public SaSsoMessage() {

View File

@ -275,7 +275,7 @@ public class SaSsoClientProcessor {
logoutParameter.setDeviceId(stpLogic.getLoginDeviceId());
}
Object centerId = ssoClientTemplate.strategy.convertLoginIdToCenterId.run(stpLogic.getLoginId());
SaSsoMessage message = ssoClientTemplate.buildSloMessage(centerId, logoutParameter);
SaSsoMessage message = ssoClientTemplate.buildSignoutMessage(centerId, logoutParameter);
SaResult result = ssoClientTemplate.pushMessageAsSaResult(message);
// 如果 sso-server 响应的状态码非200代表业务失败将回应的 msg 字段作为异常抛出

View File

@ -85,7 +85,7 @@ public class SaSsoServerProcessor {
// sso-server接收推送消息
if(req.isPath(apiName.ssoPushS)) {
return ssoPush();
return ssoPushS();
}
// 默认返回
@ -199,7 +199,7 @@ public class SaSsoServerProcessor {
*
* @return 处理结果
*/
public Object ssoPush() {
public Object ssoPushS() {
ParamName paramName = ssoServerTemplate.paramName;
SaSsoServerConfig ssoServerConfig = ssoServerTemplate.getServerConfig();

View File

@ -212,7 +212,7 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
* @param logoutParameter 单点注销
* @return 单点注销URL
*/
public SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) {
public SaSsoMessage buildSignoutMessage(Object loginId, SaLogoutParameter logoutParameter) {
SaSsoMessage message = new SaSsoMessage();
message.setType(SaSsoConsts.MESSAGE_SIGNOUT);
message.set(paramName.client, getClient());

View File

@ -28,7 +28,7 @@ import java.util.Map;
* @author click33
* @since 1.38.0
*/
public class SaSsoClientUtil extends SaSsoTemplate {
public class SaSsoClientUtil {
private SaSsoClientUtil() {
}
@ -120,8 +120,8 @@ public class SaSsoClientUtil extends SaSsoTemplate {
* @param logoutParameter 单点注销
* @return 单点注销URL
*/
public static SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) {
return SaSsoClientProcessor.instance.ssoClientTemplate.buildSloMessage(loginId, logoutParameter);
public static SaSsoMessage buildSignoutMessage(Object loginId, SaLogoutParameter logoutParameter) {
return SaSsoClientProcessor.instance.ssoClientTemplate.buildSignoutMessage(loginId, logoutParameter);
}
}

View File

@ -292,7 +292,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
*/
public SaSsoClientModel getClientNotNull(String client) {
if(SaFoxUtil.isEmpty(client)) {
if(getServerConfig().getAllowAnonClient()) {
if(getConfigOfAllowAnonClient()) {
return getAnonClient();
} else {
throw new SaSsoException("client 标识不可为空");
@ -307,7 +307,16 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
}
/**
* 获取匿名 client 信息
* 获取配置项是否允许匿名 client 接入
*
* @return /
*/
public boolean getConfigOfAllowAnonClient() {
return getServerConfig().getAllowAnonClient();
}
/**
* 获取匿名 client 配置信息
*
* @return /
*/
@ -697,8 +706,22 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
* @param message /
*/
public void pushToAllClient(SaSsoMessage message) {
pushToAllClient(message, null);
}
/**
* 向所有 Client 推送消息并忽略掉某个 client
*
* @param ignoreClient 要被忽略掉的 client null 代表不忽略
* @param message /
*/
public void pushToAllClient(SaSsoMessage message, String ignoreClient) {
// TODO 待验证
List<SaSsoClientModel> needPushClients = getNeedPushClients();
for (SaSsoClientModel client : needPushClients) {
if(ignoreClient != null && ignoreClient.equals(client.getClient())) {
continue;
}
strategy.asyncRun.run(() -> pushMessage(client, message));
}
}

View File

@ -30,7 +30,7 @@ import java.util.List;
* @author click33
* @since 1.43.0
*/
public class SaSsoServerUtil extends SaSsoTemplate {
public class SaSsoServerUtil {
private SaSsoServerUtil() {
}
@ -297,4 +297,14 @@ public class SaSsoServerUtil extends SaSsoTemplate {
SaSsoServerProcessor.instance.ssoServerTemplate.pushToAllClient(message);
}
/**
* 向所有 Client 推送消息并忽略掉某个 client
*
* @param ignoreClient 要被忽略掉的 client null 代表不忽略
* @param message /
*/
public static void pushToAllClient(SaSsoMessage message, String ignoreClient) {
SaSsoServerProcessor.instance.ssoServerTemplate.pushToAllClient(message, ignoreClient);
}
}

View File

@ -71,6 +71,7 @@ public class SaSsoBeanRegister {
* @return /
*/
@Bean
@Condition(onMissingBean = SaSsoServerTemplate.class)
public SaSsoServerTemplate getSaSsoServerTemplate() {
return SaSsoServerProcessor.instance.ssoServerTemplate;
}
@ -81,6 +82,7 @@ public class SaSsoBeanRegister {
* @return /
*/
@Bean
@Condition(onMissingBean = SaSsoClientTemplate.class)
public SaSsoClientTemplate getSaSsoClientTemplate() {
return SaSsoClientProcessor.instance.ssoClientTemplate;
}

View File

@ -23,6 +23,7 @@ import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
import cn.dev33.satoken.sso.template.SaSsoClientTemplate;
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
@ -61,6 +62,7 @@ public class SaSsoBeanRegister {
* @return /
*/
@Bean
@ConditionalOnMissingBean(SaSsoServerTemplate.class)
public SaSsoServerTemplate getSaSsoServerTemplate() {
return SaSsoServerProcessor.instance.ssoServerTemplate;
}
@ -71,6 +73,7 @@ public class SaSsoBeanRegister {
* @return /
*/
@Bean
@ConditionalOnMissingBean(SaSsoClientTemplate.class)
public SaSsoClientTemplate getSaSsoClientTemplate() {
return SaSsoClientProcessor.instance.ssoClientTemplate;
}