From 81a51bef5bc5be0e91b0f34e31d31ba8433ed4d4 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Sun, 12 Jul 2026 11:41:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(iam):=20=E6=89=93=E9=80=9A=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E7=AE=A1=E7=90=86=E9=85=8D=E7=BD=AE=E5=88=B0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TokenService.doSaLogin 读取 PlatformSessionManagementConfig 并应用: 在线时长->timeout, 活跃超时->activeTimeout, 三种并发策略(KICK_OLDEST/DENY_NEW/NEW_SESSION); enabled=false时回退yml默认; 新增activeTimeoutHours字段与concurrentLimitExceeded文案 --- .../main/resources/i18n/en-US/error/auth.json | 3 +- .../main/resources/i18n/zh-CN/error/auth.json | 3 +- .../iam/auth/service/TokenService.java | 42 +++++++++++++++++++ .../PlatformSessionManagementConfig.java | 2 + .../PlatformSessionManagementConfigParam.java | 3 ++ ...PlatformSessionManagementConfigResult.java | 3 ++ 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/auth.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/auth.json index 82a6dd2d9..ad198d8b2 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/auth.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/auth.json @@ -26,5 +26,6 @@ "tokenTimeout": "Session expired, please log in again", "beReplaced": "Account signed in on another device", "kickOut": "Session no longer valid, please log in again", - "tokenFreeze": "Authentication credential frozen" + "tokenFreeze": "Authentication credential frozen", + "concurrentLimitExceeded": "Concurrent login limit reached, please sign out from another device first" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/auth.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/auth.json index 0d57f1355..bb0f58af8 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/auth.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/auth.json @@ -26,5 +26,6 @@ "tokenTimeout": "登录已过期,请重新登录", "beReplaced": "账号已在其他设备登录", "kickOut": "登录状态已失效,请重新登录", - "tokenFreeze": "登录凭证已被冻结" + "tokenFreeze": "登录凭证已被冻结", + "concurrentLimitExceeded": "并发登录数已达上限,请先退出其他设备" } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/TokenService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/TokenService.java index 6abdc28b5..3a44eb2c7 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/TokenService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/TokenService.java @@ -19,6 +19,7 @@ import cn.daxpay.open.platform.iam.exception.auth.ApplicationNotFoundException; import cn.daxpay.open.platform.iam.result.user.UserInfoResult; import cn.daxpay.open.platform.iam.service.twofactor.UserTwoFactorService; import cn.daxpay.open.platform.iam.service.user.UserQueryService; +import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformSessionManagementConfig; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.parameter.SaLoginParameter; @@ -56,6 +57,8 @@ public class TokenService { private final UserQueryService userQueryService; + private final IamSecurityConfigService iamSecurityConfigService; + /// 登录 public String login(HttpServletRequest request, HttpServletResponse response) { AuthInfoResult authInfoResult; @@ -197,6 +200,8 @@ public class TokenService { var saLoginModel = new SaLoginParameter() .setDeviceType(clientCode) .setIsLastingCookie(true); + // 应用会话管理配置(在线时长/活跃超时/并发策略) + this.applySessionConfig(saLoginModel, authInfoResult.getId()); authInfoResult.setClient(clientCode) .setLoginType(loginType); @@ -206,6 +211,43 @@ public class TokenService { session.set(CommonCode.USER, userDetail); } + /// 应用会话管理配置到 Sa-Token 登录参数 + /// 配置未启用时跳过, 继续使用 application.yml 中的静态默认值 + private void applySessionConfig(SaLoginParameter model, Object userId) { + PlatformSessionManagementConfig config = iamSecurityConfigService.getSessionManagement(); + if (config == null || !Boolean.TRUE.equals(config.getEnabled())) { + return; + } + // 在线时长 -> token 固定有效期(秒) + if (config.getMaxOnlineHours() != null && config.getMaxOnlineHours() > 0) { + model.setTimeout(config.getMaxOnlineHours() * 3600L); + } + // 活跃超时 -> 无操作超时(秒), 0或null表示不限制 + if (config.getActiveTimeoutHours() != null && config.getActiveTimeoutHours() > 0) { + model.setActiveTimeout(config.getActiveTimeoutHours() * 3600L); + } + // 并发登录策略 + Integer max = config.getMaxConcurrentSessions(); + String strategy = config.getConcurrentStrategy(); + if ("KICK_OLDEST".equals(strategy) && max != null && max > 0) { + // 允许并发, 超出上限时 Sa-Token 自动注销最早的会话 + model.setIsConcurrent(true).setMaxLoginCount(max); + } + else if ("DENY_NEW".equals(strategy) && max != null && max > 0) { + // 登录前预检: 已达上限则拒绝新登录 + int current = StpUtil.getTokenValueListByLoginId(userId).size(); + if (current >= max) { + throw new LoginFailureException("error.auth.concurrentLimitExceeded"); + } + // 不设 maxLoginCount, 保持"拒绝"语义, 避免触发 Sa-Token 自动踢旧 + model.setIsConcurrent(true); + } + else { + // NEW_SESSION 或未配置策略: 允许并发, 不限制数量 + model.setIsConcurrent(true); + } + } + /// 退出 public void logout() { StpUtil.logout(); diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSessionManagementConfig.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSessionManagementConfig.java index bdac7ecc1..ddec966c0 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSessionManagementConfig.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSessionManagementConfig.java @@ -13,6 +13,8 @@ public class PlatformSessionManagementConfig { private Boolean enabled; /// 最大在线时长(小时) private Integer maxOnlineHours; + /// 最大活跃时长(小时), 0或null表示不限制 + private Integer activeTimeoutHours; /// 最大并发会话数 private Integer maxConcurrentSessions; /// 并发会话策略 diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSessionManagementConfigParam.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSessionManagementConfigParam.java index 402b59fb5..e78198045 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSessionManagementConfigParam.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSessionManagementConfigParam.java @@ -17,6 +17,9 @@ public class PlatformSessionManagementConfigParam { @Schema(description = "最大在线时长(小时)") private Integer maxOnlineHours; + @Schema(description = "最大活跃时长(小时),0或留空表示不限制") + private Integer activeTimeoutHours; + @Schema(description = "最大并发会话数") private Integer maxConcurrentSessions; diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSessionManagementConfigResult.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSessionManagementConfigResult.java index 679173fe1..f88f9d9ac 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSessionManagementConfigResult.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSessionManagementConfigResult.java @@ -20,6 +20,9 @@ public class PlatformSessionManagementConfigResult extends BaseResult { @Schema(description = "最大在线时长(小时)") private Integer maxOnlineHours; + @Schema(description = "最大活跃时长(小时),0或留空表示不限制") + private Integer activeTimeoutHours; + @Schema(description = "最大并发会话数") private Integer maxConcurrentSessions;