✨ 登录配置化.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## v1.0.1
|
||||
|
||||
`2024-03-` `release`
|
||||
`2024-03-06` `release`
|
||||
|
||||
🐞 修复 用户操作日志条件重置后类型框数据不正常的问题
|
||||
🩰 修改 主机连接日志 UI
|
||||
|
||||
@@ -6,3 +6,9 @@
|
||||
* 断开: 断开连接
|
||||
* 删除: 删除连接记录
|
||||
* 清理: 根据条件清理数据
|
||||
|
||||
### SFTP 操作日志
|
||||
|
||||
查看用户 SFTP 操作日志, 是从用户操作日志中过滤查询。
|
||||
|
||||
* 删除: 删除操作日志
|
||||
|
||||
@@ -25,3 +25,4 @@
|
||||
记录用户在系统内的操作日志。
|
||||
|
||||
* 详情: 查看操作的参数以及留痕信息
|
||||
* 清理: 根据条件清理数据
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -165,6 +166,18 @@ public class RedisUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置过期时间
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param unit unit
|
||||
*/
|
||||
public static void setExpire(String key, long timeout, TimeUnit unit) {
|
||||
// 设置过期时间
|
||||
redisTemplate.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
public static void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
|
||||
if (RedisUtils.redisTemplate != null) {
|
||||
// unmodified
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "app.authentication",
|
||||
"type": "com.orion.ops.module.infra.config.AppAuthenticationConfig",
|
||||
"sourceType": "com.orion.ops.module.infra.config.AppAuthenticationConfig"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "app.authentication.allowMultiDevice",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否允许多端登录."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.allowRefresh",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否允许凭证续签."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.maxRefreshCount",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "凭证续签最大次数."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.loginFailedLockCount",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "登录失败锁定次数."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.loginFailedLockTime",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "登录失败锁定时间 (分)."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -118,6 +118,21 @@ logging:
|
||||
level:
|
||||
com.orion.ops.launch.controller.BootstrapController: INFO
|
||||
|
||||
# 应用配置
|
||||
app:
|
||||
authentication:
|
||||
# 是否允许多端登录
|
||||
allow-multi-device: true
|
||||
# 是否允许凭证续签
|
||||
allow-refresh: true
|
||||
# 凭证续签最大次数
|
||||
max-refresh-count: 3
|
||||
# 登录失败锁定次数
|
||||
login-failed-lock-count: 5
|
||||
# 登录失败锁定时间 (分)
|
||||
login-failed-lock-time: 30
|
||||
|
||||
# orion framework config
|
||||
orion:
|
||||
# 版本
|
||||
version: @revision@
|
||||
@@ -151,7 +166,6 @@ orion:
|
||||
asset:
|
||||
group: "asset - 资产模块"
|
||||
path: "asset"
|
||||
|
||||
logging:
|
||||
# 全局日志打印
|
||||
printer:
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.orion.ops.module.infra.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 应用认证配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/5 18:26
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties("app.authentication")
|
||||
public class AppAuthenticationConfig {
|
||||
|
||||
/**
|
||||
* 是否允许多端登录
|
||||
*/
|
||||
private Boolean allowMultiDevice;
|
||||
|
||||
/**
|
||||
* 是否允许凭证续签
|
||||
*/
|
||||
private Boolean allowRefresh;
|
||||
|
||||
/**
|
||||
* 凭证续签最大次数
|
||||
*/
|
||||
private Integer maxRefreshCount;
|
||||
|
||||
/**
|
||||
* 登录失败锁定次数
|
||||
*/
|
||||
private Integer loginFailedLockCount;
|
||||
|
||||
/**
|
||||
* 登录失败锁定时间 (分)
|
||||
*/
|
||||
private Integer loginFailedLockTime;
|
||||
|
||||
}
|
||||
@@ -38,7 +38,6 @@ public interface UserCacheKeyDefine {
|
||||
.desc("用户登录失败次数 ${username}")
|
||||
.type(Integer.class)
|
||||
.struct(RedisCacheStruct.STRING)
|
||||
.timeout(3, TimeUnit.DAYS)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine LOGIN_TOKEN = new CacheKeyBuilder()
|
||||
|
||||
@@ -16,16 +16,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*/
|
||||
public interface AuthenticationService {
|
||||
|
||||
// TODO 配置化
|
||||
// 允许多端登录
|
||||
boolean allowMultiDevice = true;
|
||||
// 允许凭证续签
|
||||
boolean allowRefresh = true;
|
||||
// 凭证续签最大次数
|
||||
int maxRefreshCount = 3;
|
||||
// 失败锁定次数
|
||||
int maxFailedLoginCount = 5;
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisStrings;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisUtils;
|
||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.ops.module.infra.config.AppAuthenticationConfig;
|
||||
import com.orion.ops.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.ops.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.ops.module.infra.dao.SystemUserRoleDAO;
|
||||
@@ -39,6 +40,7 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -51,6 +53,9 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
|
||||
@Resource
|
||||
private AppAuthenticationConfig appAuthenticationConfig;
|
||||
|
||||
@Resource
|
||||
private SystemUserDAO systemUserDAO;
|
||||
|
||||
@@ -95,7 +100,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
String userAgent = Servlets.getUserAgent(servletRequest);
|
||||
long current = System.currentTimeMillis();
|
||||
// 不允许多端登录
|
||||
if (!allowMultiDevice) {
|
||||
if (!appAuthenticationConfig.getAllowMultiDevice()) {
|
||||
// 无效化其他缓存
|
||||
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location, userAgent);
|
||||
}
|
||||
@@ -157,7 +162,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
return JSON.parseObject(loginCache, LoginTokenDTO.class);
|
||||
}
|
||||
// loginToken 不存在 需要查询 refreshToken
|
||||
if (!allowRefresh) {
|
||||
if (!appAuthenticationConfig.getAllowRefresh()) {
|
||||
return null;
|
||||
}
|
||||
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(pair.getKey(), pair.getValue());
|
||||
@@ -172,7 +177,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
refresh.setRefreshCount(refreshCount);
|
||||
// 设置登录缓存
|
||||
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, refresh);
|
||||
if (refreshCount < maxRefreshCount) {
|
||||
if (refreshCount < appAuthenticationConfig.getMaxRefreshCount()) {
|
||||
// 小于续签最大次数 则再次设置 refreshToken
|
||||
RedisStrings.setJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH, refresh);
|
||||
} else {
|
||||
@@ -214,7 +219,8 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
// 检查登录失败次数
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
|
||||
if (failedCount != null && Integer.parseInt(failedCount) >= maxFailedLoginCount) {
|
||||
if (failedCount != null
|
||||
&& Integer.parseInt(failedCount) >= appAuthenticationConfig.getLoginFailedLockCount()) {
|
||||
throw Exceptions.argument(ErrorMessage.MAX_LOGIN_FAILED);
|
||||
}
|
||||
}
|
||||
@@ -235,23 +241,23 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
// 刷新登录失败缓存
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
Long failedLoginCount = redisTemplate.opsForValue().increment(failedCountKey);
|
||||
RedisUtils.setExpire(failedCountKey, UserCacheKeyDefine.LOGIN_FAILED_COUNT);
|
||||
// 锁定用户
|
||||
if (failedLoginCount >= maxFailedLoginCount) {
|
||||
// 更新用户表
|
||||
SystemUserDO updateUser = new SystemUserDO();
|
||||
updateUser.setId(user.getId());
|
||||
updateUser.setStatus(UserStatusEnum.LOCKED.getStatus());
|
||||
systemUserDAO.updateById(updateUser);
|
||||
// 修改缓存状态
|
||||
String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
|
||||
String userInfoCache = redisTemplate.opsForValue().get(userInfoKey);
|
||||
if (userInfoCache != null) {
|
||||
LoginUser loginUser = JSON.parseObject(userInfoCache, LoginUser.class);
|
||||
loginUser.setStatus(UserStatusEnum.LOCKED.getStatus());
|
||||
RedisStrings.setJson(userInfoKey, UserCacheKeyDefine.USER_INFO, loginUser);
|
||||
}
|
||||
}
|
||||
RedisUtils.setExpire(failedCountKey, appAuthenticationConfig.getLoginFailedLockTime(), TimeUnit.MINUTES);
|
||||
// // 锁定用户
|
||||
// if (failedLoginCount >= appAuthenticationConfig.getLoginFailedLockCount()) {
|
||||
// // 更新用户表
|
||||
// SystemUserDO updateUser = new SystemUserDO();
|
||||
// updateUser.setId(user.getId());
|
||||
// updateUser.setStatus(UserStatusEnum.LOCKED.getStatus());
|
||||
// systemUserDAO.updateById(updateUser);
|
||||
// // 修改缓存状态
|
||||
// String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
|
||||
// String userInfoCache = redisTemplate.opsForValue().get(userInfoKey);
|
||||
// if (userInfoCache != null) {
|
||||
// LoginUser loginUser = JSON.parseObject(userInfoCache, LoginUser.class);
|
||||
// loginUser.setStatus(UserStatusEnum.LOCKED.getStatus());
|
||||
// RedisStrings.setJson(userInfoKey, UserCacheKeyDefine.USER_INFO, loginUser);
|
||||
// }
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -337,7 +343,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
}
|
||||
}
|
||||
// 删除续签信息
|
||||
if (allowRefresh) {
|
||||
if (appAuthenticationConfig.getAllowRefresh()) {
|
||||
RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*"));
|
||||
}
|
||||
}
|
||||
@@ -365,7 +371,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
.build();
|
||||
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginValue);
|
||||
// 生成 refreshToken
|
||||
if (allowRefresh) {
|
||||
if (appAuthenticationConfig.getAllowRefresh()) {
|
||||
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, loginTime);
|
||||
RedisStrings.setJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH, loginValue);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.orion.ops.framework.redis.core.utils.RedisStrings;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisUtils;
|
||||
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
|
||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.ops.module.infra.config.AppAuthenticationConfig;
|
||||
import com.orion.ops.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.ops.module.infra.dao.OperatorLogDAO;
|
||||
import com.orion.ops.module.infra.dao.SystemRoleDAO;
|
||||
@@ -49,6 +50,9 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class SystemUserServiceImpl implements SystemUserService {
|
||||
|
||||
@Resource
|
||||
private AppAuthenticationConfig appAuthenticationConfig;
|
||||
|
||||
@Resource
|
||||
private SystemUserDAO systemUserDAO;
|
||||
|
||||
@@ -274,7 +278,7 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
// 删除登录缓存
|
||||
RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*"));
|
||||
// 删除续签信息
|
||||
if (AuthenticationService.allowRefresh) {
|
||||
if (appAuthenticationConfig.getAllowRefresh()) {
|
||||
RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user