Pre Merge pull request !349 from 石泽旭/dev

This commit is contained in:
石泽旭 2025-08-05 05:48:42 +00:00 committed by Gitee
commit c6e19e09f1
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
5 changed files with 93 additions and 6 deletions

View File

@ -17,6 +17,7 @@ package cn.dev33.satoken.config;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutRange;
import cn.dev33.satoken.stp.parameter.enums.SaRepeatLoginsMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
import cn.dev33.satoken.util.SaFoxUtil;
@ -64,6 +65,11 @@ public class SaTokenConfig implements Serializable {
*/
private Boolean isShare = false;
/**
* isConcurrent=false 多客户端登录时的处理策略
*/
private SaRepeatLoginsMode repeatLoginsMode = SaRepeatLoginsMode.KICKOUT;
/**
* isConcurrent=false 顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端, ALL_DEVICE_TYPE=所有设备类型端)
*/
@ -713,6 +719,22 @@ public class SaTokenConfig implements Serializable {
return this;
}
/**
* @return 不允许并发登录时重复登录处理策略
*/
public SaRepeatLoginsMode getRepeatLoginsMode() {
return repeatLoginsMode;
}
/**
* @param repeatLoginsMode 不允许并发登录时重复登录处理策略
* @return 对象自身
*/
public SaTokenConfig setRepeatLoginsMode(SaRepeatLoginsMode repeatLoginsMode) {
this.repeatLoginsMode = repeatLoginsMode;
return this;
}
/**
* 获取 isConcurrent=false 顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端 ALL_DEVICE_TYPE=所有设备类型端)
*
@ -860,6 +882,7 @@ public class SaTokenConfig implements Serializable {
+ ", isConcurrent=" + isConcurrent
+ ", isShare=" + isShare
+ ", replacedRange=" + replacedRange
+ ", repeatLoginsMode=" + repeatLoginsMode
+ ", maxLoginCount=" + maxLoginCount
+ ", overflowLogoutMode=" + overflowLogoutMode
+ ", maxTryTimes=" + maxTryTimes

View File

@ -80,6 +80,9 @@ public interface SaErrorCode {
/** 更改 Token 指向的 账号Id 时账号Id值为空 */
int CODE_11003 = 11003;
/** 当前账号已经登录 */
int CODE_11004 = 11004;
/** 未能读取到有效Token */
int CODE_11011 = 11011;

View File

@ -36,6 +36,7 @@ import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.dev33.satoken.stp.parameter.SaLogoutParameter;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutRange;
import cn.dev33.satoken.stp.parameter.enums.SaRepeatLoginsMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
@ -538,13 +539,27 @@ public class StpLogic {
protected String distUsableToken(Object id, SaLoginParameter loginParameter) {
// 1获取全局配置的 isConcurrent 参数
// 如果配置为不允许一个账号多地同时登录则需要先将这个账号的历史登录会话标记为被顶下线
// 如果配置为不允许一个账号多地同时登录则需要根据配置选择
// .将这个账号的历史登录会话标记为被顶下线
// .提示错误并拒绝本次登录
if( ! loginParameter.getIsConcurrent()) {
if(loginParameter.getReplacedRange() == SaReplacedRange.CURR_DEVICE_TYPE) {
replaced(id, loginParameter.getDeviceType());
}
if(loginParameter.getReplacedRange() == SaReplacedRange.ALL_DEVICE_TYPE) {
replaced(id, createSaLogoutParameter());
if (loginParameter.getRepeatLoginsMode() == SaRepeatLoginsMode.KICKOUT){
if(loginParameter.getReplacedRange() == SaReplacedRange.CURR_DEVICE_TYPE) {
replaced(id, loginParameter.getDeviceType());
}
if(loginParameter.getReplacedRange() == SaReplacedRange.ALL_DEVICE_TYPE) {
replaced(id, createSaLogoutParameter());
}
} else if (loginParameter.getRepeatLoginsMode() == SaRepeatLoginsMode.INTERCEPT){
List<SaTerminalInfo> terminalListByLoginId = getTerminalListByLoginId(id);
// 只有当存在有效的会话时才拒绝登录
boolean hasActiveSession = terminalListByLoginId.stream()
.anyMatch(terminal -> isValidToken(terminal.getTokenValue()));
if (hasActiveSession) {
throw new SaTokenException("当前账号已在其他客户端登录").setCode(SaErrorCode.CODE_11004);
}
}
}

View File

@ -21,6 +21,7 @@ import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
import cn.dev33.satoken.stp.parameter.enums.SaRepeatLoginsMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
import cn.dev33.satoken.util.SaTokenConsts;
@ -110,6 +111,11 @@ public class SaLoginParameter {
*/
private Boolean isWriteHeader;
/**
* isConcurrent=false 多客户端登录时的处理策略
*/
private SaRepeatLoginsMode repeatLoginsMode;
/**
* isConcurrent=false 顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端, ALL_DEVICE_TYPE=所有设备类型端)
*/
@ -158,6 +164,7 @@ public class SaLoginParameter {
this.replacedRange = config.getReplacedRange();
this.overflowLogoutMode = config.getOverflowLogoutMode();
this.rightNowCreateTokenSession = config.getRightNowCreateTokenSession();
this.repeatLoginsMode = config.getRepeatLoginsMode();
this.setupCookieConfig(cookie -> {
SaCookieConfig gCookie = config.getCookie();
@ -568,6 +575,23 @@ public class SaLoginParameter {
return this;
}
/**
* 获取多客户端登录时的处理策略
* @return 多客户端登录时的处理策略
*/
public SaRepeatLoginsMode getRepeatLoginsMode() {
return repeatLoginsMode;
}
/**
* 设置多客户端登录时的处理策略
* @param repeatLoginsMode 多客户端登录时的处理策略
*/
public void setRepeatLoginsMode(SaRepeatLoginsMode repeatLoginsMode) {
this.repeatLoginsMode = repeatLoginsMode;
}
/*
* toString
*/
@ -577,6 +601,7 @@ public class SaLoginParameter {
+ "deviceType=" + deviceType
+ ", deviceId=" + deviceId
+ ", replacedRange=" + replacedRange
+ ", repeatLoginsMode=" + repeatLoginsMode
+ ", overflowLogoutMode=" + overflowLogoutMode
+ ", isLastingCookie=" + isLastingCookie
+ ", timeout=" + timeout

View File

@ -0,0 +1,21 @@
package cn.dev33.satoken.stp.parameter.enums;
/**
* SaRepeatLoginsMode: 重复登录模式
* @author 石泽旭
* @since 1.44.0
*/
public enum SaRepeatLoginsMode {
/**
* 将旧会话踢出
*/
KICKOUT,
/**
* 拦截新的登录会话
*/
INTERCEPT
}