新增sso client 登录记录功能

This commit is contained in:
click33 2024-05-01 03:37:18 +08:00
parent 08659f1fa8
commit 7652a51592
7 changed files with 237 additions and 40 deletions

View File

@ -316,7 +316,7 @@ public class SaSsoClientConfig implements Serializable {
@Override
public String toString() {
return "SaSsoConfig ["
return "SaSsoClientConfig ["
+ "mode=" + mode
+ ", client=" + client
+ ", serverUrl=" + serverUrl

View File

@ -64,6 +64,11 @@ public class SaSsoServerConfig implements Serializable {
*/
public Boolean isHttp = false;
/**
* Access-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
*/
public int maxRegClient = 32;
/**
* 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true
*/
@ -154,6 +159,22 @@ public class SaSsoServerConfig implements Serializable {
return this;
}
/**
* @return maxLoginClient Access-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
*/
public int getMaxRegClient() {
return maxRegClient;
}
/**
* @param maxRegClient Access-Session 上记录 Client 信息的最高数量-1=无限超过此值将进行自动清退处理先进先出
* @return 对象自身
*/
public SaSsoServerConfig setMaxRegClient(int maxRegClient) {
this.maxRegClient = maxRegClient;
return this;
}
/**
* 获取 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true
*
@ -191,6 +212,7 @@ public class SaSsoServerConfig implements Serializable {
+ ", allowUrl=" + allowUrl
+ ", isSlo=" + isSlo
+ ", isHttp=" + isHttp
+ ", maxRegClient=" + maxRegClient
+ ", isCheckSign=" + isCheckSign
+ "]";
}

View File

@ -24,10 +24,20 @@ package cn.dev33.satoken.sso.model;
*/
public class SaSsoClientModel {
/**
* client 登录模式1=模式一2=模式二3=模式三
*/
public int mode;
/**
* 客户端标识
*/
public String client;
public String client;
/**
* 此次登录 token
*/
public String tokenValue;
/**
* 单点注销回调url
@ -37,7 +47,12 @@ public class SaSsoClientModel {
/**
* client 注册信息的时间13位时间戳
*/
public Long regTime;
public long regTime;
/**
* 此账号有记录以来为第几次登录默认从0开始递增
*/
public int index;
public SaSsoClientModel() {
}
@ -48,6 +63,27 @@ public class SaSsoClientModel {
this.regTime = System.currentTimeMillis();
}
/**
* 获取 client 登录模式1=模式一2=模式二3=模式三
*
* @return mode client 登录模式1=模式一2=模式二3=模式三
*/
public int getMode() {
return this.mode;
}
/**
* 设置 client 登录模式1=模式一2=模式二3=模式三
*
* @param mode client 登录模式1=模式一2=模式二3=模式三
* @return /
*/
public SaSsoClientModel setMode(int mode) {
this.mode = mode;
return this;
}
/**
* 获取 客户端标识
*
@ -61,9 +97,31 @@ public class SaSsoClientModel {
* 设置 客户端标识
*
* @param client 客户端标识
* @return /
*/
public void setClient(String client) {
public SaSsoClientModel setClient(String client) {
this.client = client;
return this;
}
/**
* 获取 此次登录 token
*
* @return tokenValue 此次登录 token
*/
public String getTokenValue() {
return this.tokenValue;
}
/**
* 设置 此次登录 token
*
* @param tokenValue 此次登录 token
* @return /
*/
public SaSsoClientModel setTokenValue(String tokenValue) {
this.tokenValue = tokenValue;
return this;
}
/**
@ -79,9 +137,11 @@ public class SaSsoClientModel {
* 设置 单点注销回调url
*
* @param ssoLogoutCall 单点注销回调url
* @return /
*/
public void setSsoLogoutCall(String ssoLogoutCall) {
public SaSsoClientModel setSsoLogoutCall(String ssoLogoutCall) {
this.ssoLogoutCall = ssoLogoutCall;
return this;
}
/**
@ -89,7 +149,7 @@ public class SaSsoClientModel {
*
* @return regTime client 注册信息的时间13位时间戳
*/
public Long getRegTime() {
public long getRegTime() {
return this.regTime;
}
@ -97,17 +157,42 @@ public class SaSsoClientModel {
* 设置 client 注册信息的时间13位时间戳
*
* @param regTime client 注册信息的时间13位时间戳
* @return /
*/
public void setRegTime(Long regTime) {
public SaSsoClientModel setRegTime(long regTime) {
this.regTime = regTime;
return this;
}
/**
* 获取 此账号有记录以来为第几次登录默认从0开始递增
*
* @return regTime 此账号有记录以来为第几次登录默认从0开始递增
*/
public long getIndex() {
return this.index;
}
/**
* 设置 此账号有记录以来为第几次登录默认从0开始递增
*
* @param index 此账号有记录以来为第几次登录默认从0开始递增
* @return /
*/
public SaSsoClientModel setIndex(int index) {
this.index = index;
return this;
}
@Override
public String toString() {
return "SaSsoClientModel{" +
"client='" + client + '\'' +
"mode=" + mode +
", client='" + client + '\'' +
", tokenValue='" + tokenValue + '\'' +
", ssoLogoutCall='" + ssoLogoutCall + '\'' +
", regTime='" + regTime + '\'' +
", regTime=" + regTime +
", index=" + index +
'}';
}

View File

@ -47,6 +47,9 @@ public class ParamName {
/** Client端单点注销时-回调URL 参数名称 */
public String ssoLogoutCall = "ssoLogoutCall";
/** 是否为超过 maxRegClient 的自动注销 */
public String autoLogout = "autoLogout";
public String name = "name";
public String pwd = "pwd";

View File

@ -283,13 +283,25 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
SaSession session = getStpLogic().getSessionByLoginId(loginId);
//
// 出原来的
List<SaSsoClientModel> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
//
scmList.add(new SaSsoClientModel(client, sloCallbackUrl));
// 新登录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);
}
@ -305,31 +317,85 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
return;
}
SaSsoServerConfig ssoConfig = getServerConfig();
// step.1 遍历通知 Client 端注销会话
List<SaSsoClientModel> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
scmList.forEach(scm -> {
// url
String sloCallUrl = scm.getSsoLogoutCall();
// 参数
Map<String, Object> paramsMap = new TreeMap<>();
paramsMap.put(paramName.client, scm.getClient());
paramsMap.put(paramName.loginId, loginId);
String signParamsStr = getSignTemplate(scm.getClient()).addSignParamsAndJoin(paramsMap);
// 拼接
String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr);
// 发起请求
ssoConfig.sendHttp.apply(finalUrl);
notifyClientLogout(loginId, scm, false);
});
// step.2 Server端注销
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
* @param scmList /
* @return /
*/
private int calcNextIndex(List<SaSsoClientModel> 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
return maxIndex++;
}
// ---------------------- 构建URL ----------------------

View File

@ -45,4 +45,11 @@ public class SaSsoConsts {
/** 表示请求没有得到任何有效处理 {msg: "not handle"} */
public static final String NOT_HANDLE = "{\"msg\": \"not handle\"}";
/** SSO 模式1 */
public static final int SSO_MODE_1 = 1;
/** SSO 模式2 */
public static final int SSO_MODE_2 = 2;
/** SSO 模式3 */
public static final int SSO_MODE_3 = 3;
}

View File

@ -15,11 +15,13 @@
*/
package cn.dev33.satoken.solon.sso;
import cn.dev33.satoken.config.SaSsoConfig;
import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.sso.SaSsoProcessor;
import cn.dev33.satoken.sso.template.SaSsoTemplate;
import cn.dev33.satoken.sso.template.SaSsoUtil;
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
import cn.dev33.satoken.sso.template.SaSsoClientTemplate;
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Condition;
import org.noear.solon.annotation.Configuration;
@ -40,21 +42,33 @@ public class SaSsoAutoConfigure implements InitializingBean {
@Override
public void afterInjection() throws Throwable {
appContext.subBeansOfType(SaSsoTemplate.class, bean->{
SaSsoUtil.ssoTemplate = bean;
SaSsoProcessor.instance.ssoTemplate = bean;
appContext.subBeansOfType(SaSsoServerTemplate.class, bean->{
SaSsoServerProcessor.instance.ssoServerTemplate = bean;
});
appContext.subBeansOfType(SaSsoClientTemplate.class, bean->{
SaSsoClientProcessor.instance.ssoClientTemplate = bean;
});
appContext.subBeansOfType(SaSsoConfig.class, bean->{
SaSsoManager.setConfig(bean);
appContext.subBeansOfType(SaSsoServerConfig.class, bean->{
SaSsoManager.setServerConfig(bean);
});
appContext.subBeansOfType(SaSsoClientConfig.class, bean->{
SaSsoManager.setClientConfig(bean);
});
}
/**
* 获取 SSO 配置Bean
* 获取 SSO Server 配置Bean
* */
@Bean
public SaSsoConfig getConfig(@Inject(value = "${sa-token.sso}",required = false) SaSsoConfig ssoConfig) {
public SaSsoServerConfig getConfig(@Inject(value = "${sa-token.sso-server}",required = false) SaSsoServerConfig ssoConfig) {
return ssoConfig;
}
/**
* 获取 SSO Client 配置Bean
* */
@Bean
public SaSsoClientConfig getClientConfig(@Inject(value = "${sa-token.sso-client}",required = false) SaSsoClientConfig ssoConfig) {
return ssoConfig;
}
}