发布 v1.10 #5
@@ -51,9 +51,10 @@ public final class AuthLoginSessionPolicy {
|
|||||||
/**
|
/**
|
||||||
* 判断 Web 登录是否需要替换既有 Web 会话。
|
* 判断 Web 登录是否需要替换既有 Web 会话。
|
||||||
*
|
*
|
||||||
* @return 始终为 true
|
* @param multiLogin 是否允许账号多端登录
|
||||||
|
* @return 禁止多端登录时返回 true
|
||||||
*/
|
*/
|
||||||
public static boolean shouldReplaceExistingWebSession() {
|
public static boolean shouldReplaceExistingWebSession(boolean multiLogin) {
|
||||||
return true;
|
return !multiLogin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ import tech.easyflow.common.constant.Constants;
|
|||||||
import tech.easyflow.common.constant.enums.EnumDataStatus;
|
import tech.easyflow.common.constant.enums.EnumDataStatus;
|
||||||
import tech.easyflow.common.entity.LoginAccount;
|
import tech.easyflow.common.entity.LoginAccount;
|
||||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||||
import tech.easyflow.system.entity.SysApiKey;
|
import tech.easyflow.system.config.AccountSecurityProperties;
|
||||||
import tech.easyflow.system.entity.SysAccount;
|
import tech.easyflow.system.entity.SysAccount;
|
||||||
|
import tech.easyflow.system.entity.SysApiKey;
|
||||||
import tech.easyflow.system.entity.SysMenu;
|
import tech.easyflow.system.entity.SysMenu;
|
||||||
import tech.easyflow.system.entity.SysRole;
|
import tech.easyflow.system.entity.SysRole;
|
||||||
import tech.easyflow.system.service.SysApiKeyService;
|
|
||||||
import tech.easyflow.system.service.SysAccountService;
|
import tech.easyflow.system.service.SysAccountService;
|
||||||
|
import tech.easyflow.system.service.SysApiKeyService;
|
||||||
import tech.easyflow.system.service.SysMenuService;
|
import tech.easyflow.system.service.SysMenuService;
|
||||||
import tech.easyflow.system.service.SysRoleService;
|
import tech.easyflow.system.service.SysRoleService;
|
||||||
import tech.easyflow.system.util.SysPasswordPolicy;
|
import tech.easyflow.system.util.SysPasswordPolicy;
|
||||||
@@ -54,6 +55,8 @@ public class AuthServiceImpl implements AuthService, StpInterface {
|
|||||||
private SysApiKeyService sysApiKeyService;
|
private SysApiKeyService sysApiKeyService;
|
||||||
@Resource
|
@Resource
|
||||||
private RedisLockExecutor redisLockExecutor;
|
private RedisLockExecutor redisLockExecutor;
|
||||||
|
@Resource
|
||||||
|
private AccountSecurityProperties accountSecurityProperties;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoginVO login(LoginDTO loginDTO) {
|
public LoginVO login(LoginDTO loginDTO) {
|
||||||
@@ -171,22 +174,23 @@ public class AuthServiceImpl implements AuthService, StpInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建互斥的 Web 登录会话。
|
* 根据账号安全策略创建 Web 登录会话。
|
||||||
*
|
*
|
||||||
* @param record 登录账号
|
* @param record 登录账号
|
||||||
* @return 登录结果
|
* @return 登录结果
|
||||||
*/
|
*/
|
||||||
private LoginVO createWebLoginVO(SysAccount record) {
|
private LoginVO createWebLoginVO(SysAccount record) {
|
||||||
|
if (!AuthLoginSessionPolicy.shouldReplaceExistingWebSession(accountSecurityProperties.isMultiLogin())) {
|
||||||
|
return createLoginVO(record, AuthLoginSessionPolicy.webLoginModel());
|
||||||
|
}
|
||||||
return redisLockExecutor.executeWithLock(
|
return redisLockExecutor.executeWithLock(
|
||||||
WEB_LOGIN_LOCK_KEY_PREFIX + record.getId(),
|
WEB_LOGIN_LOCK_KEY_PREFIX + record.getId(),
|
||||||
WEB_LOGIN_LOCK_WAIT,
|
WEB_LOGIN_LOCK_WAIT,
|
||||||
WEB_LOGIN_LOCK_LEASE,
|
WEB_LOGIN_LOCK_LEASE,
|
||||||
() -> {
|
() -> {
|
||||||
if (AuthLoginSessionPolicy.shouldReplaceExistingWebSession()) {
|
|
||||||
StpUtil.replaced(record.getId(), AuthLoginSessionPolicy.WEB_DEVICE);
|
StpUtil.replaced(record.getId(), AuthLoginSessionPolicy.WEB_DEVICE);
|
||||||
// 首次升级后的新登录同时淘汰旧版本未标记设备类型的浏览器会话。
|
// 首次升级后的新登录同时淘汰旧版本未标记设备类型的浏览器会话。
|
||||||
StpUtil.replaced(record.getId(), AuthLoginSessionPolicy.LEGACY_WEB_DEVICE);
|
StpUtil.replaced(record.getId(), AuthLoginSessionPolicy.LEGACY_WEB_DEVICE);
|
||||||
}
|
|
||||||
return createLoginVO(record, AuthLoginSessionPolicy.webLoginModel());
|
return createLoginVO(record, AuthLoginSessionPolicy.webLoginModel());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.junit.Test;
|
|||||||
public class AuthLoginSessionPolicyTest {
|
public class AuthLoginSessionPolicyTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证 Web 登录使用独立设备类型并要求替换旧会话。
|
* 验证 Web 登录设备类型和多端登录会话替换策略。
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void shouldBuildExclusiveWebLoginModel() {
|
public void shouldBuildExclusiveWebLoginModel() {
|
||||||
@@ -18,7 +18,8 @@ public class AuthLoginSessionPolicyTest {
|
|||||||
|
|
||||||
Assert.assertEquals("WEB", model.getDevice());
|
Assert.assertEquals("WEB", model.getDevice());
|
||||||
Assert.assertEquals("default-device", AuthLoginSessionPolicy.LEGACY_WEB_DEVICE);
|
Assert.assertEquals("default-device", AuthLoginSessionPolicy.LEGACY_WEB_DEVICE);
|
||||||
Assert.assertTrue(AuthLoginSessionPolicy.shouldReplaceExistingWebSession());
|
Assert.assertTrue(AuthLoginSessionPolicy.shouldReplaceExistingWebSession(false));
|
||||||
|
Assert.assertFalse(AuthLoginSessionPolicy.shouldReplaceExistingWebSession(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ public class AccountSecurityProperties implements InitializingBean {
|
|||||||
*/
|
*/
|
||||||
private String defaultResetPassword = "!QAZ2wsx";
|
private String defaultResetPassword = "!QAZ2wsx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许同一账号同时在多个客户端登录。
|
||||||
|
*/
|
||||||
|
private boolean multiLogin = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取默认重置密码。
|
* 获取默认重置密码。
|
||||||
*
|
*
|
||||||
@@ -35,6 +40,24 @@ public class AccountSecurityProperties implements InitializingBean {
|
|||||||
this.defaultResetPassword = defaultResetPassword;
|
this.defaultResetPassword = defaultResetPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否允许同一账号同时在多个客户端登录。
|
||||||
|
*
|
||||||
|
* @return 允许多端登录时返回 true
|
||||||
|
*/
|
||||||
|
public boolean isMultiLogin() {
|
||||||
|
return multiLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否允许同一账号同时在多个客户端登录。
|
||||||
|
*
|
||||||
|
* @param multiLogin 是否允许多端登录
|
||||||
|
*/
|
||||||
|
public void setMultiLogin(boolean multiLogin) {
|
||||||
|
this.multiLogin = multiLogin;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用启动时校验默认密码符合系统强密码策略。
|
* 应用启动时校验默认密码符合系统强密码策略。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,6 +19,19 @@ public class AccountSecurityPropertiesTest {
|
|||||||
properties.afterPropertiesSet();
|
properties.afterPropertiesSet();
|
||||||
|
|
||||||
Assert.assertEquals("!QAZ2wsx", properties.getDefaultResetPassword());
|
Assert.assertEquals("!QAZ2wsx", properties.getDefaultResetPassword());
|
||||||
|
Assert.assertFalse(properties.isMultiLogin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证账号多端登录配置可以开启。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldEnableAccountMultiLogin() {
|
||||||
|
AccountSecurityProperties properties = new AccountSecurityProperties();
|
||||||
|
|
||||||
|
properties.setMultiLogin(true);
|
||||||
|
|
||||||
|
Assert.assertTrue(properties.isMultiLogin());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ easyflow:
|
|||||||
cron: "0 30 2 * * *"
|
cron: "0 30 2 * * *"
|
||||||
security:
|
security:
|
||||||
account:
|
account:
|
||||||
default-reset-password: '${EASYFLOW_DEFAULT_RESET_PASSWORD:!QAZ2wsx}'
|
default-reset-password: "!QAZ2wsx"
|
||||||
|
multi-login: false
|
||||||
license:
|
license:
|
||||||
location: classpath:easyflow.lic
|
location: classpath:easyflow.lic
|
||||||
chat:
|
chat:
|
||||||
|
|||||||
Reference in New Issue
Block a user