sso 模块新增 maxRegClient 属性,用于控制模式三下 client 注册数量

This commit is contained in:
click33
2024-05-01 05:53:18 +08:00
parent 7652a51592
commit 14a97a1fcb
10 changed files with 144 additions and 138 deletions

View File

@@ -13,9 +13,9 @@ public class SaSso2ClientApplication {
System.out.println(); System.out.println();
System.out.println("---------------------- Sa-Token SSO 模式二 Client 端启动成功 ----------------------"); System.out.println("---------------------- Sa-Token SSO 模式二 Client 端启动成功 ----------------------");
System.out.println("配置信息:" + SaSsoManager.getClientConfig()); System.out.println("配置信息:" + SaSsoManager.getClientConfig());
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9001"); System.out.println("测试访问应用端一: http://sa-sso-client1.com:9002");
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9001"); System.out.println("测试访问应用端二: http://sa-sso-client2.com:9002");
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9001"); System.out.println("测试访问应用端三: http://sa-sso-client3.com:9002");
System.out.println("测试前需要根据官网文档修改hosts文件测试账号密码sa / 123456"); System.out.println("测试前需要根据官网文档修改hosts文件测试账号密码sa / 123456");
System.out.println(); System.out.println();
} }

View File

@@ -1,9 +1,11 @@
# 端口 # 端口
server: server:
port: 9001 port: 9002
# sa-token配置 # sa-token配置
sa-token: sa-token:
# 每次登录时产生不同的token
is-share: false
# SSO-相关配置 # SSO-相关配置
sso-client: sso-client:
# SSO-Server端 统一认证地址 # SSO-Server端 统一认证地址

View File

@@ -13,9 +13,9 @@ public class SaSso3ClientApplication {
System.out.println(); System.out.println();
System.out.println("---------------------- Sa-Token SSO 模式三 Client 端启动成功 ----------------------"); System.out.println("---------------------- Sa-Token SSO 模式三 Client 端启动成功 ----------------------");
System.out.println("配置信息:" + SaSsoManager.getClientConfig()); System.out.println("配置信息:" + SaSsoManager.getClientConfig());
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9001"); System.out.println("测试访问应用端一: http://sa-sso-client1.com:9003");
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9001"); System.out.println("测试访问应用端二: http://sa-sso-client2.com:9003");
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9001"); System.out.println("测试访问应用端三: http://sa-sso-client3.com:9003");
System.out.println("测试前需要根据官网文档修改hosts文件测试账号密码sa / 123456"); System.out.println("测试前需要根据官网文档修改hosts文件测试账号密码sa / 123456");
System.out.println(); System.out.println();
} }

View File

@@ -1,6 +1,6 @@
# 端口 # 端口
server: server:
port: 9001 port: 9003
# sa-token配置 # sa-token配置
sa-token: sa-token:

View File

@@ -16,6 +16,8 @@
package cn.dev33.satoken.sso.model; package cn.dev33.satoken.sso.model;
import cn.dev33.satoken.sso.util.SaSsoConsts;
/** /**
* Sa-Token SSO Model * Sa-Token SSO Model
* *
@@ -24,6 +26,11 @@ package cn.dev33.satoken.sso.model;
*/ */
public class SaSsoClientModel { public class SaSsoClientModel {
/*
* 只能记录模式三登录的 client 信息,模式一和模式二的信息即使记录上,也无法完成单点注销操作,遂不记录
* 所以mode、tokenValue 字段,仅留作扩展,暂时无用
*/
/** /**
* 此 client 登录模式1=模式一2=模式二3=模式三) * 此 client 登录模式1=模式一2=模式二3=模式三)
*/ */
@@ -34,15 +41,15 @@ public class SaSsoClientModel {
*/ */
public String client; public String client;
/** // /**
* 此次登录 token 值 // * 此次登录 token 值
*/ // */
public String tokenValue; // public String tokenValue;
/** /**
* 单点注销回调url * 单点注销回调url
*/ */
public String ssoLogoutCall; public String sloCallbackUrl;
/** /**
* 此 client 注册信息的时间13位时间戳 * 此 client 注册信息的时间13位时间戳
@@ -57,10 +64,15 @@ public class SaSsoClientModel {
public SaSsoClientModel() { public SaSsoClientModel() {
} }
public SaSsoClientModel(String client, String ssoLogoutCall) { /**
* 模式三构建
*/
public SaSsoClientModel(String client, String sloCallbackUrl, int index) {
this.mode = SaSsoConsts.SSO_MODE_3;
this.client = client; this.client = client;
this.ssoLogoutCall = ssoLogoutCall; this.sloCallbackUrl = sloCallbackUrl;
this.regTime = System.currentTimeMillis(); this.regTime = System.currentTimeMillis();
this.index = index;
} }
@@ -104,43 +116,43 @@ public class SaSsoClientModel {
return this; return this;
} }
/** // /**
* 获取 此次登录 token 值 // * 获取 此次登录 token 值
* // *
* @return tokenValue 此次登录 token 值 // * @return tokenValue 此次登录 token 值
*/ // */
public String getTokenValue() { // public String getTokenValue() {
return this.tokenValue; // return this.tokenValue;
} // }
//
/** // /**
* 设置 此次登录 token 值 // * 设置 此次登录 token 值
* // *
* @param tokenValue 此次登录 token 值 // * @param tokenValue 此次登录 token 值
* @return / // * @return /
*/ // */
public SaSsoClientModel setTokenValue(String tokenValue) { // public SaSsoClientModel setTokenValue(String tokenValue) {
this.tokenValue = tokenValue; // this.tokenValue = tokenValue;
return this; // return this;
} // }
/** /**
* 获取 单点注销回调url * 获取 单点注销回调url
* *
* @return ssoLogoutCall 单点注销回调url * @return ssoLogoutCall 单点注销回调url
*/ */
public String getSsoLogoutCall() { public String getSloCallbackUrl() {
return this.ssoLogoutCall; return this.sloCallbackUrl;
} }
/** /**
* 设置 单点注销回调url * 设置 单点注销回调url
* *
* @param ssoLogoutCall 单点注销回调url * @param sloCallbackUrl 单点注销回调url
* @return / * @return /
*/ */
public SaSsoClientModel setSsoLogoutCall(String ssoLogoutCall) { public SaSsoClientModel setSloCallbackUrl(String sloCallbackUrl) {
this.ssoLogoutCall = ssoLogoutCall; this.sloCallbackUrl = sloCallbackUrl;
return this; return this;
} }
@@ -189,8 +201,8 @@ public class SaSsoClientModel {
return "SaSsoClientModel{" + return "SaSsoClientModel{" +
"mode=" + mode + "mode=" + mode +
", client='" + client + '\'' + ", client='" + client + '\'' +
", tokenValue='" + tokenValue + '\'' + // ", tokenValue='" + tokenValue + '\'' +
", ssoLogoutCall='" + ssoLogoutCall + '\'' + ", sloCallbackUrl='" + sloCallbackUrl + '\'' +
", regTime=" + regTime + ", regTime=" + regTime +
", index=" + index + ", index=" + index +
'}'; '}';

View File

@@ -227,10 +227,13 @@ public class SaSsoClientProcessor {
// 获取参数 // 获取参数
String loginId = req.getParamNotNull(paramName.loginId); String loginId = req.getParamNotNull(paramName.loginId);
// String client = req.getParam(paramName.client);
// String autoLogout = req.getParam(paramName.autoLogout);
// 校验参数签名 // 校验参数签名
if(ssoConfig.getIsCheckSign()) { if(ssoConfig.getIsCheckSign()) {
ssoClientTemplate.getSignTemplate(ssoConfig.getClient()).checkRequest(req, paramName.loginId); ssoClientTemplate.getSignTemplate(ssoConfig.getClient()).
checkRequest(req, paramName.loginId, paramName.client, paramName.autoLogout);
} else { } else {
SaSsoManager.printNoCheckSignWarningByRuntime(); SaSsoManager.printNoCheckSignWarningByRuntime();
} }
@@ -289,7 +292,11 @@ public class SaSsoClientProcessor {
} }
} else { } else {
// q2、使用模式二直连Redis校验ticket // q2、使用模式二直连Redis校验ticket
// return ssoClientTemplate.checkTicket(ticket); // 注意此处调用了 SaSsoServerProcessor 处理器里的方法,
// 这意味着如果你的 sso-server 端重写了 SaSsoServerProcessor 里的部分方法,
// 而在当前 sso-client 没有按照相应格式重写 SaSsoClientProcessor 里的方法,
// 可能会导致调用失败(注意是可能,而非一定),
// 解决方案为:在当前 sso-client 端也按照 sso-server 端的格式重写 SaSsoClientProcessor 里的方法
return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, cfg.getClient()); return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, cfg.getClient());
} }
} }

View File

@@ -42,6 +42,8 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return SaSsoManager.getClientConfig(); return SaSsoManager.getClientConfig();
} }
// ------------------- SSO 模式三相关 ------------------- // ------------------- SSO 模式三相关 -------------------
/** /**

View File

@@ -17,12 +17,12 @@ package cn.dev33.satoken.sso.template;
import cn.dev33.satoken.SaManager; import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.sso.SaSsoManager; import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.config.SaSsoServerConfig; import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.error.SaSsoErrorCode; import cn.dev33.satoken.sso.error.SaSsoErrorCode;
import cn.dev33.satoken.sso.exception.SaSsoException; import cn.dev33.satoken.sso.exception.SaSsoException;
import cn.dev33.satoken.sso.model.SaSsoClientModel; import cn.dev33.satoken.sso.model.SaSsoClientModel;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
@@ -266,44 +266,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
} }
// ------------------- SSO -------------------
// ------------------- SSO 模式三相关 -------------------
/**
* 为指定账号id注册单点注销回调URL
* @param loginId 账号id
* @param client 指定客户端标识可为null
* @param sloCallbackUrl 单点注销时的回调URL
*/
public void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) {
// 如果提供的参数是空值,则直接返回,不进行任何操作
if(SaFoxUtil.isEmpty(loginId) || SaFoxUtil.isEmpty(sloCallbackUrl)) {
return;
}
SaSession session = getStpLogic().getSessionByLoginId(loginId);
// 取出原来的
List<SaSsoClientModel> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
// 将 新登录client 加入到集合中
SaSsoClientModel scm = new SaSsoClientModel();
scm.mode = 3;
scm.client = client;
scm.ssoLogoutCall = sloCallbackUrl;
scm.regTime = System.currentTimeMillis();
scm.index = calcNextIndex(scmList);
scmList.add(scm);
// 如果登录的client数量超过了限制则将最早的一个登录进行清退
if(scmList.size() > getServerConfig().getMaxRegClient()) {
SaSsoClientModel removeScm = scmList.remove(0);
notifyClientLogout(loginId, removeScm, true);
}
// 存入持久库
session.set(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, scmList);
}
/** /**
* 指定账号单点注销 * 指定账号单点注销
@@ -327,60 +290,12 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
getStpLogic().logout(loginId); getStpLogic().logout(loginId);
} }
/**
* 通知指定账号的指定客户端注销
* @param loginId 指定账号
* @param scm 客户端信息对象
* @param autoLogout 是否为超过 maxRegClient 的自动注销
*/
public void notifyClientLogout(Object loginId, SaSsoClientModel scm, boolean autoLogout) {
// 如果给个null值不进行任何操作
if(scm == null) {
return;
}
// 如果是模式二登录的
if(scm.mode == SaSsoConsts.SSO_MODE_2) {
// 获取登录 token
String tokenValue = scm.tokenValue;
if(SaFoxUtil.isEmpty(tokenValue)) {
return;
}
// 注销此 token
getStpLogic().logoutByTokenValue(scm.tokenValue);
}
// 如果是模式三登录的
else if(scm.mode != SaSsoConsts.SSO_MODE_3) {
// url
String sloCallUrl = scm.getSsoLogoutCall();
if(SaFoxUtil.isEmpty(sloCallUrl)) {
return;
}
// 参数
Map<String, Object> paramsMap = new TreeMap<>();
paramsMap.put(paramName.client, scm.getClient());
paramsMap.put(paramName.loginId, loginId);
paramsMap.put(paramName.autoLogout, autoLogout);
String signParamsStr = getSignTemplate(scm.getClient()).addSignParamsAndJoin(paramsMap);
// 拼接
String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr);
// 发起请求
getServerConfig().sendHttp.apply(finalUrl);
}
}
/** /**
* 计算下一个 index 值 * 计算下一个 index 值
* @param scmList / * @param scmList /
* @return / * @return /
*/ */
private int calcNextIndex(List<SaSsoClientModel> scmList) { public int calcNextIndex(List<SaSsoClientModel> scmList) {
// 如果目前还没有任何登录记录则直接返回0 // 如果目前还没有任何登录记录则直接返回0
if(scmList == null || scmList.isEmpty()) { if(scmList == null || scmList.isEmpty()) {
return 0; return 0;
@@ -394,7 +309,79 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
} }
// 否则返回最大值+1 // 否则返回最大值+1
return maxIndex++; maxIndex++;
return maxIndex;
}
/**
* 为指定账号id注册单点注销回调信息模式三
* @param loginId 账号id
* @param client 指定客户端标识可为null
* @param sloCallbackUrl 单点注销时的回调URL
*/
public void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) {
// 如果提供的参数是空值,则直接返回,不进行任何操作
if(SaFoxUtil.isEmpty(loginId)) {
return;
}
SaSession session = getStpLogic().getSessionByLoginId(loginId);
// 取出原来的
List<SaSsoClientModel> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
// 将 新登录client 加入到集合中
SaSsoClientModel scm = new SaSsoClientModel(client, sloCallbackUrl, calcNextIndex(scmList));
scmList.add(scm);
// 如果登录的client数量超过了限制则从最早的一个登录开始清退
int maxRegClient = getServerConfig().maxRegClient;
if(maxRegClient != -1) {
for (;;) {
if(scmList.size() > maxRegClient) {
SaSsoClientModel removeScm = scmList.remove(0);
notifyClientLogout(loginId, removeScm, true);
} else {
break;
}
}
}
// 存入持久库
session.set(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, scmList);
}
/**
* 通知指定账号的指定客户端注销
* @param loginId 指定账号
* @param scm 客户端信息对象
* @param autoLogout 是否为超过 maxRegClient 的自动注销
*/
public void notifyClientLogout(Object loginId, SaSsoClientModel scm, boolean autoLogout) {
// 如果给个null值不进行任何操作
if(scm == null || scm.mode != SaSsoConsts.SSO_MODE_3) {
return;
}
// url
String sloCallUrl = scm.getSloCallbackUrl();
if(SaFoxUtil.isEmpty(sloCallUrl)) {
return;
}
// 参数
Map<String, Object> paramsMap = new TreeMap<>();
paramsMap.put(paramName.client, scm.getClient());
paramsMap.put(paramName.loginId, loginId);
paramsMap.put(paramName.autoLogout, autoLogout);
String signParamsStr = getSignTemplate(scm.getClient()).addSignParamsAndJoin(paramsMap);
// 拼接
String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr);
// 发起请求
getServerConfig().sendHttp.apply(finalUrl);
} }
// ---------------------- 构建URL ---------------------- // ---------------------- 构建URL ----------------------

View File

@@ -21,9 +21,6 @@ import cn.dev33.satoken.sso.name.ApiName;
import cn.dev33.satoken.sso.name.ParamName; import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/** /**
* Sa-Token SSO 模板方法类 (公共端) * Sa-Token SSO 模板方法类 (公共端)
@@ -81,5 +78,4 @@ public class SaSsoTemplate {
return SaManager.getSaSignTemplate(); return SaManager.getSaSignTemplate();
} }
} }

View File

@@ -144,7 +144,7 @@ public class SaSsoUtil {
} }
/** /**
* 指定账号单点注销 * 指定账号单点注销 (以Server方发起)
* @param loginId 指定账号 * @param loginId 指定账号
*/ */
public static void ssoLogout(Object loginId) { public static void ssoLogout(Object loginId) {