From 808c3cdc0f12fbe5207a0ed0e963f8e69c36efcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E6=B3=BD=E6=97=AD?= <2435963663@qq.com> Date: Thu, 31 Jul 2025 15:07:20 +0800 Subject: [PATCH] =?UTF-8?q?--=E6=B7=BB=E5=8A=A0=E9=87=8D=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=A4=84=E7=90=86=E7=AD=96=E7=95=A5=EF=BC=8C=E5=BD=93?= =?UTF-8?q?=E4=B8=8D=E5=85=81=E8=AE=B8=E5=A4=9A=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E7=99=BB=E5=BD=95=E6=97=B6=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E6=98=AF=E8=B8=A2=E4=BA=BA=E4=B8=8B=E7=BA=BF?= =?UTF-8?q?=E8=BF=98=E6=98=AF=E6=8B=A6=E6=88=AA=E6=9C=AC=E6=AC=A1=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev33/satoken/config/SaTokenConfig.java | 23 +++++++++++++++++ .../cn/dev33/satoken/error/SaErrorCode.java | 3 +++ .../java/cn/dev33/satoken/stp/StpLogic.java | 24 +++++++++++++----- .../stp/parameter/SaLoginParameter.java | 25 +++++++++++++++++++ .../parameter/enums/SaRepeatLoginsMode.java | 21 ++++++++++++++++ 5 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/enums/SaRepeatLoginsMode.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java index 239c1337..7db8c218 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java @@ -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 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java b/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java index bd5e329a..c6cd3e93 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java @@ -80,6 +80,9 @@ public interface SaErrorCode { /** 更改 Token 指向的 账号Id 时,账号Id值为空 */ int CODE_11003 = 11003; + /** 当前账号已经登录 */ + int CODE_11004 = 11004; + /** 未能读取到有效Token */ int CODE_11011 = 11011; diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java index 539b4497..7a504486 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -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,14 +539,25 @@ 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 terminalListByLoginId = getTerminalListByLoginId(id); + if(!terminalListByLoginId.isEmpty()) { + throw new SaTokenException("当前账号已在其他客户端登录").setCode(SaErrorCode.CODE_11004); + } + } + } // 2、如果调用者预定了要生成的 token,则直接返回这个预定的值,框架无需再操心了 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/SaLoginParameter.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/SaLoginParameter.java index ee3c4329..9ff21882 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/SaLoginParameter.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/SaLoginParameter.java @@ -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 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/enums/SaRepeatLoginsMode.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/enums/SaRepeatLoginsMode.java new file mode 100644 index 00000000..8458c883 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/parameter/enums/SaRepeatLoginsMode.java @@ -0,0 +1,21 @@ +package cn.dev33.satoken.stp.parameter.enums; + +/** + * SaRepeatLoginsMode: 重复登录模式 + * @author 石泽旭 + * @since 1.44.0 + */ +public enum SaRepeatLoginsMode { + + /** + * 将旧会话踢出 + */ + KICKOUT, + + /** + * 拦截新的登录会话 + */ + INTERCEPT + + +}