From 83c64dddfb405cf7ce592a253b6d81d0f3e9b190 Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Thu, 30 Oct 2025 16:42:43 +0800 Subject: [PATCH] =?UTF-8?q?:hammer:=20=E4=BF=AE=E6=94=B9=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/api/impl/AuthenticationApiImpl.java | 7 +- .../controller/AuthenticationController.java | 5 +- .../infra/entity/dto/LoginTokenDTO.java | 19 +- .../infra/enums/LoginTokenStatusEnum.java | 10 +- .../impl/SecurityFrameworkServiceImpl.java | 5 +- .../infra/service/AuthenticationService.java | 21 +- .../impl/AuthenticationServiceImpl.java | 189 +++++++++--------- .../service/impl/SystemUserServiceImpl.java | 8 +- 8 files changed, 144 insertions(+), 120 deletions(-) diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/api/impl/AuthenticationApiImpl.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/api/impl/AuthenticationApiImpl.java index 2d7bee05..40d4d94a 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/api/impl/AuthenticationApiImpl.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/api/impl/AuthenticationApiImpl.java @@ -25,6 +25,7 @@ package org.dromara.visor.module.infra.api.impl; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.common.constant.ErrorMessage; import org.dromara.visor.common.utils.Assert; +import org.dromara.visor.common.utils.Requests; import org.dromara.visor.module.infra.api.AuthenticationApi; import org.dromara.visor.module.infra.entity.domain.SystemUserDO; import org.dromara.visor.module.infra.entity.dto.user.SystemUserAuthDTO; @@ -57,7 +58,11 @@ public class AuthenticationApiImpl implements AuthenticationApi { result.setUsername(user.getUsername()); result.setNickname(user.getNickname()); // 检查用户密码 - boolean passRight = authenticationService.checkUserPassword(user, password, addFailedCount); + boolean passRight = authenticationService.checkUserPassword(user, password); + if (!passRight && addFailedCount) { + // 发送站内信 + authenticationService.addLoginFailedCount(user.getUsername(), Requests.getIdentity()); + } result.setPassRight(passRight); Assert.isTrue(passRight, ErrorMessage.USERNAME_PASSWORD_ERROR); // 检查用户状态 diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java index 4c1ab5ec..e9b227a6 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java @@ -63,9 +63,8 @@ public class AuthenticationController { @PermitAll @Operation(summary = "登录") @PostMapping("/login") - public UserLoginVO login(@Validated @RequestBody UserLoginRequest request, - HttpServletRequest servletRequest) { - return authenticationService.login(request, servletRequest); + public UserLoginVO login(@Validated @RequestBody UserLoginRequest request) { + return authenticationService.login(request); } @OperatorLog(AuthenticationOperatorType.LOGOUT) diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/dto/LoginTokenDTO.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/dto/LoginTokenDTO.java index 9488338d..967d55a2 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/dto/LoginTokenDTO.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/dto/LoginTokenDTO.java @@ -26,7 +26,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.dromara.visor.module.infra.enums.LoginTokenStatusEnum; +import org.dromara.visor.common.entity.RequestIdentityModel; /** * 登录 token 缓存 @@ -42,14 +42,19 @@ import org.dromara.visor.module.infra.enums.LoginTokenStatusEnum; public class LoginTokenDTO { /** - * 用户id + * userId */ private Long id; + /** + * 用户名 + */ + private String username; + /** * token 状态 * - * @see LoginTokenStatusEnum + * @see org.dromara.visor.module.infra.enums.LoginTokenStatusEnum */ private Integer status; @@ -59,13 +64,13 @@ public class LoginTokenDTO { private Integer refreshCount; /** - * 原始登录身份 + * 原始登录留痕信息 */ - private LoginTokenIdentityDTO origin; + private RequestIdentityModel origin; /** - * 覆盖登录身份 + * 覆盖登录刘海信息 */ - private LoginTokenIdentityDTO override; + private RequestIdentityModel override; } diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/LoginTokenStatusEnum.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/LoginTokenStatusEnum.java index 0fcfc3df..32e0fb69 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/LoginTokenStatusEnum.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/LoginTokenStatusEnum.java @@ -26,8 +26,8 @@ import cn.orionsec.kit.lang.utils.time.Dates; import lombok.AllArgsConstructor; import lombok.Getter; import org.dromara.visor.common.constant.ErrorCode; +import org.dromara.visor.common.entity.RequestIdentityModel; import org.dromara.visor.module.infra.entity.dto.LoginTokenDTO; -import org.dromara.visor.module.infra.entity.dto.LoginTokenIdentityDTO; import java.util.Date; @@ -53,9 +53,9 @@ public enum LoginTokenStatusEnum { OTHER_DEVICE(1) { @Override public RuntimeException toException(LoginTokenDTO token) { - LoginTokenIdentityDTO override = token.getOverride(); + RequestIdentityModel override = token.getOverride(); return ErrorCode.USER_OTHER_DEVICE_LOGIN.exception( - Dates.format(new Date(override.getLoginTime()), Dates.MD_HM), + Dates.format(new Date(override.getTimestamp()), Dates.MD_HM), override.getAddress(), override.getLocation()); } @@ -68,9 +68,9 @@ public enum LoginTokenStatusEnum { SESSION_OFFLINE(2) { @Override public RuntimeException toException(LoginTokenDTO token) { - LoginTokenIdentityDTO override = token.getOverride(); + RequestIdentityModel override = token.getOverride(); return ErrorCode.USER_OFFLINE.exception( - Dates.format(new Date(override.getLoginTime()), Dates.MD_HM), + Dates.format(new Date(override.getTimestamp()), Dates.MD_HM), override.getAddress(), override.getLocation()); } diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/framework/service/impl/SecurityFrameworkServiceImpl.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/framework/service/impl/SecurityFrameworkServiceImpl.java index 7676fb26..f60b5269 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/framework/service/impl/SecurityFrameworkServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/framework/service/impl/SecurityFrameworkServiceImpl.java @@ -82,12 +82,13 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { if (tokenInfo == null) { return null; } + Long loginTime = tokenInfo.getOrigin().getTimestamp(); try { // 检查 token 状态 this.checkTokenStatus(tokenInfo); } catch (Exception e) { // token 失效则删除 - RedisUtils.delete(UserCacheKeyDefine.LOGIN_TOKEN.format(tokenInfo.getId(), tokenInfo.getOrigin().getLoginTime())); + RedisUtils.delete(UserCacheKeyDefine.LOGIN_TOKEN.format(tokenInfo.getId(), loginTime)); throw e; } // 获取登录信息 @@ -98,7 +99,7 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { // 检查用户状态 UserStatusEnum.checkUserStatus(user.getStatus()); // 设置登录时间戳 - user.setTimestamp(tokenInfo.getOrigin().getLoginTime()); + user.setTimestamp(loginTime); return user; } diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/AuthenticationService.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/AuthenticationService.java index 99a083ae..39a502a7 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/AuthenticationService.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/AuthenticationService.java @@ -22,6 +22,7 @@ */ package org.dromara.visor.module.infra.service; +import org.dromara.visor.common.entity.RequestIdentityModel; import org.dromara.visor.common.security.LoginUser; import org.dromara.visor.module.infra.entity.domain.SystemUserDO; import org.dromara.visor.module.infra.entity.dto.LoginTokenDTO; @@ -42,11 +43,10 @@ public interface AuthenticationService { /** * 登录 * - * @param request request - * @param servletRequest servletRequest + * @param request request * @return login */ - UserLoginVO login(UserLoginRequest request, HttpServletRequest servletRequest); + UserLoginVO login(UserLoginRequest request); /** * 登出 @@ -83,12 +83,19 @@ public interface AuthenticationService { /** * 检查用户密码 * - * @param user user - * @param password password - * @param addFailedCount addFailedCount + * @param user user + * @param password password * @return passRight */ - boolean checkUserPassword(SystemUserDO user, String password, boolean addFailedCount); + boolean checkUserPassword(SystemUserDO user, String password); + + /** + * 添加登录失败次数 + * + * @param username username + * @param identity identity + */ + void addLoginFailedCount(String username, RequestIdentityModel identity); /** * 检查用户状态 diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/AuthenticationServiceImpl.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/AuthenticationServiceImpl.java index 86d134eb..85543cae 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/AuthenticationServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/AuthenticationServiceImpl.java @@ -22,29 +22,26 @@ */ package org.dromara.visor.module.infra.service.impl; -import cn.orionsec.kit.lang.annotation.Keep; import cn.orionsec.kit.lang.define.wrapper.Pair; import cn.orionsec.kit.lang.utils.Booleans; import cn.orionsec.kit.lang.utils.Exceptions; -import cn.orionsec.kit.lang.utils.Strings; -import cn.orionsec.kit.lang.utils.collect.Lists; import cn.orionsec.kit.lang.utils.crypto.Signatures; import cn.orionsec.kit.lang.utils.time.Dates; -import cn.orionsec.kit.web.servlet.web.Servlets; import com.alibaba.fastjson.JSON; import org.dromara.visor.common.config.ConfigStore; import org.dromara.visor.common.constant.ConfigKeys; import org.dromara.visor.common.constant.Const; import org.dromara.visor.common.constant.ErrorMessage; import org.dromara.visor.common.constant.ExtraFieldConst; +import org.dromara.visor.common.entity.RequestIdentity; +import org.dromara.visor.common.entity.RequestIdentityModel; import org.dromara.visor.common.security.LoginUser; import org.dromara.visor.common.security.UserRole; import org.dromara.visor.common.utils.AesEncryptUtils; import org.dromara.visor.common.utils.Assert; -import org.dromara.visor.common.utils.IpUtils; +import org.dromara.visor.common.utils.Requests; import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; import org.dromara.visor.framework.redis.core.utils.RedisStrings; -import org.dromara.visor.framework.redis.core.utils.RedisUtils; import org.dromara.visor.framework.security.core.utils.SecurityUtils; import org.dromara.visor.module.common.config.AppLoginConfig; import org.dromara.visor.module.infra.api.SystemMessageApi; @@ -54,8 +51,8 @@ import org.dromara.visor.module.infra.dao.SystemUserRoleDAO; import org.dromara.visor.module.infra.define.cache.UserCacheKeyDefine; import org.dromara.visor.module.infra.define.message.SystemUserMessageDefine; import org.dromara.visor.module.infra.entity.domain.SystemUserDO; +import org.dromara.visor.module.infra.entity.dto.LoginFailedDTO; import org.dromara.visor.module.infra.entity.dto.LoginTokenDTO; -import org.dromara.visor.module.infra.entity.dto.LoginTokenIdentityDTO; import org.dromara.visor.module.infra.entity.dto.message.SystemMessageDTO; import org.dromara.visor.module.infra.entity.request.user.UserLoginRequest; import org.dromara.visor.module.infra.entity.vo.UserLoginVO; @@ -63,7 +60,6 @@ import org.dromara.visor.module.infra.enums.LoginTokenStatusEnum; import org.dromara.visor.module.infra.enums.UserStatusEnum; import org.dromara.visor.module.infra.service.AuthenticationService; import org.dromara.visor.module.infra.service.UserPermissionService; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; @@ -98,10 +94,6 @@ public class AuthenticationServiceImpl implements AuthenticationService { @Resource private SystemMessageApi systemMessageApi; - @Keep - @Resource - private RedisTemplate redisTemplate; - @Resource private ConfigStore configStore; @@ -110,25 +102,29 @@ public class AuthenticationServiceImpl implements AuthenticationService { // 监听并且设置缓存过期时间 configStore.int32(ConfigKeys.LOGIN_LOGIN_SESSION_TIME).onChange((v, b) -> this.setCacheExpireTime()); configStore.int32(ConfigKeys.LOGIN_REFRESH_INTERVAL).onChange((v, b) -> this.setCacheExpireTime()); + configStore.int32(ConfigKeys.LOGIN_LOGIN_FAILED_LOCK_TIME).onChange((v, b) -> this.setCacheExpireTime()); this.setCacheExpireTime(); } @Override - public UserLoginVO login(UserLoginRequest request, HttpServletRequest servletRequest) { - // 获取登录信息 - String remoteAddr = IpUtils.getRemoteAddr(servletRequest); - String location = IpUtils.getLocation(remoteAddr); - String userAgent = Servlets.getUserAgent(servletRequest); + public UserLoginVO login(UserLoginRequest request) { + // 获取登录痕迹 + String username = request.getUsername(); + RequestIdentityModel identity = Requests.getIdentity(); // 设置日志上下文的用户 否则登录失败不会记录日志 OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(request)); // 登录前检查 - SystemUserDO user = this.preCheckLogin(request.getUsername(), request.getPassword()); + SystemUserDO user = this.preCheckLogin(username, request.getPassword()); // 重新设置日志上下文 OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(user)); // 用户密码校验 - boolean passRight = this.checkUserPassword(user, request.getPassword(), true); - // 发送站内信 - this.sendLoginFailedErrorMessage(passRight, user, remoteAddr, location); + boolean passRight = this.checkUserPassword(user, request.getPassword()); + if (!passRight) { + // 增加登录失败次数 + this.addLoginFailedCount(username, identity); + // 登录失败发送站内信 + this.sendLoginFailedErrorMessage(user, identity); + } Assert.isTrue(passRight, ErrorMessage.USERNAME_PASSWORD_ERROR); // 用户状态校验 this.checkUserStatus(user); @@ -139,14 +135,13 @@ public class AuthenticationServiceImpl implements AuthenticationService { this.deleteUserCache(user); // 重设用户缓存 this.setUserCache(user); - long current = System.currentTimeMillis(); // 不允许多端登录 if (Booleans.isFalse(appLoginConfig.getAllowMultiDevice())) { // 无效化其他缓存 - this.invalidOtherDeviceToken(id, current, remoteAddr, location, userAgent); + this.invalidOtherDeviceToken(id, identity); } // 生成 loginToken - String token = this.generatorLoginToken(user, current, remoteAddr, location, userAgent); + String token = this.generatorLoginToken(user, identity); return UserLoginVO.builder() .token(token) .build(); @@ -169,16 +164,16 @@ public class AuthenticationServiceImpl implements AuthenticationService { // 删除 loginToken & refreshToken String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, current); String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, current); - redisTemplate.delete(Lists.of(loginKey, refreshKey)); + RedisStrings.delete(loginKey, refreshKey); } @Override public LoginUser getLoginUser(Long id) { + // 查询缓存用户信息 String userInfoKey = UserCacheKeyDefine.USER_INFO.format(id); - String userInfoCache = redisTemplate.opsForValue().get(userInfoKey); - // 缓存存在 - if (userInfoCache != null) { - return JSON.parseObject(userInfoCache, LoginUser.class); + LoginUser loginUser = RedisStrings.getJson(userInfoKey, UserCacheKeyDefine.USER_INFO); + if (loginUser != null) { + return loginUser; } // 查询用户信息 SystemUserDO user = systemUserDAO.selectById(id); @@ -198,22 +193,21 @@ public class AuthenticationServiceImpl implements AuthenticationService { } // 获取登录 key value String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(pair.getKey(), pair.getValue()); - String loginCache = redisTemplate.opsForValue().get(loginKey); + LoginTokenDTO loginCache = RedisStrings.getJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN); if (loginCache != null) { - return JSON.parseObject(loginCache, LoginTokenDTO.class); + return loginCache; } // loginToken 不存在 需要查询 refreshToken if (Booleans.isFalse(appLoginConfig.getAllowRefresh())) { return null; } String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(pair.getKey(), pair.getValue()); - String refreshCache = redisTemplate.opsForValue().get(refreshKey); - // 未查询到刷新key直接返回 - if (refreshCache == null) { + LoginTokenDTO refresh = RedisStrings.getJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH); + // 未查询到 refreshToken 直接返回 + if (refresh == null) { return null; } // 执行续签操作 - LoginTokenDTO refresh = JSON.parseObject(refreshCache, LoginTokenDTO.class); int refreshCount = refresh.getRefreshCount() + 1; refresh.setRefreshCount(refreshCount); // 设置登录缓存 @@ -223,7 +217,7 @@ public class AuthenticationServiceImpl implements AuthenticationService { RedisStrings.setJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH, refresh); } else { // 大于等于续签最大次数 则删除 - redisTemplate.delete(refreshKey); + RedisStrings.delete(refreshKey); } return refresh; } @@ -236,11 +230,14 @@ public class AuthenticationServiceImpl implements AuthenticationService { } // 检查登录失败次数锁定 if (Booleans.isTrue(appLoginConfig.getLoginFailedLock())) { - String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(username); - String failedCount = redisTemplate.opsForValue().get(failedCountKey); - if (failedCount != null - && Integer.parseInt(failedCount) >= appLoginConfig.getLoginFailedLockThreshold()) { - throw Exceptions.argument(ErrorMessage.MAX_LOGIN_FAILED); + String loginFailedKey = UserCacheKeyDefine.LOGIN_FAILED.format(username); + LoginFailedDTO loginFailed = RedisStrings.getJson(loginFailedKey, UserCacheKeyDefine.LOGIN_FAILED); + Integer failedCount = Optional.ofNullable(loginFailed) + .map(LoginFailedDTO::getFailedCount) + .orElse(null); + // 检查是否超过失败次数 + if (failedCount != null && failedCount >= appLoginConfig.getLoginFailedLockThreshold()) { + Assert.lt(failedCount, appLoginConfig.getLoginFailedLockThreshold(), ErrorMessage.MAX_LOGIN_FAILED); } } // 获取登录用户 @@ -254,16 +251,32 @@ public class AuthenticationServiceImpl implements AuthenticationService { } @Override - public boolean checkUserPassword(SystemUserDO user, String password, boolean addFailedCount) { - // 检查密码 - boolean passRight = user.getPassword().equals(Signatures.md5(password)); - if (!passRight && addFailedCount) { - // 刷新登录失败缓存 - String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername()); - redisTemplate.opsForValue().increment(failedCountKey); - RedisUtils.setExpire(failedCountKey, appLoginConfig.getLoginFailedLockTime(), TimeUnit.MINUTES); + public boolean checkUserPassword(SystemUserDO user, String password) { + return user.getPassword().equals(Signatures.md5(password)); + } + + @Override + public void addLoginFailedCount(String username, RequestIdentityModel identity) { + // 过期时间 + long expireTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(UserCacheKeyDefine.LOGIN_FAILED.getTimeout()); + // 刷新登录失败缓存 + String loginFailedKey = UserCacheKeyDefine.LOGIN_FAILED.format(username); + LoginFailedDTO loginFailed = RedisStrings.getJson(loginFailedKey, UserCacheKeyDefine.LOGIN_FAILED); + if (loginFailed == null) { + // 首次登录失败 + loginFailed = LoginFailedDTO.builder() + .username(username) + .failedCount(1) + .expireTime(expireTime) + .origin(identity) + .build(); + } else { + // 非首次登录失败 + loginFailed.setExpireTime(expireTime); + loginFailed.setFailedCount(loginFailed.getFailedCount() + 1); } - return passRight; + // 重新设置缓存 + RedisStrings.setJson(loginFailedKey, UserCacheKeyDefine.LOGIN_FAILED, loginFailed); } @Override @@ -275,33 +288,30 @@ public class AuthenticationServiceImpl implements AuthenticationService { /** * 发送登录失败错误消息 * - * @param passRight passRight - * @param user user - * @param remoteAddr remoteAddr - * @param location location + * @param user user + * @param identity identity */ - private void sendLoginFailedErrorMessage(boolean passRight, SystemUserDO user, - String remoteAddr, String location) { - if (passRight) { - return; - } + private void sendLoginFailedErrorMessage(SystemUserDO user, RequestIdentity identity) { // 检查是否开启登录失败发信 if (!Booleans.isTrue(appLoginConfig.getLoginFailedSend())) { return; } - String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername()); - String failedCountStr = redisTemplate.opsForValue().get(failedCountKey); - if (failedCountStr == null || !Strings.isInteger(failedCountStr)) { + String loginFailedKey = UserCacheKeyDefine.LOGIN_FAILED.format(user.getUsername()); + LoginFailedDTO loginFailed = RedisStrings.getJson(loginFailedKey, UserCacheKeyDefine.LOGIN_FAILED); + Integer failedCount = Optional.ofNullable(loginFailed) + .map(LoginFailedDTO::getFailedCount) + .orElse(null); + if (failedCount == null) { return; } // 直接用相等 因为只触发一次 - if (!Integer.valueOf(failedCountStr).equals(appLoginConfig.getLoginFailedSendThreshold())) { + if (!failedCount.equals(appLoginConfig.getLoginFailedSendThreshold())) { return; } // 发送站内信 Map params = new HashMap<>(); - params.put(ExtraFieldConst.ADDRESS, remoteAddr); - params.put(ExtraFieldConst.LOCATION, location); + params.put(ExtraFieldConst.ADDRESS, identity.getAddress()); + params.put(ExtraFieldConst.LOCATION, identity.getLocation()); params.put(ExtraFieldConst.TIME, Dates.current()); SystemMessageDTO message = SystemMessageDTO.builder() .receiverId(user.getId()) @@ -334,9 +344,9 @@ public class AuthenticationServiceImpl implements AuthenticationService { // 用户信息缓存 String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId()); // 登录失败次数缓存 - String loginFailedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername()); + String loginFailedCountKey = UserCacheKeyDefine.LOGIN_FAILED.format(user.getUsername()); // 删除缓存 - redisTemplate.delete(Lists.of(userInfoKey, loginFailedCountKey)); + RedisStrings.delete(userInfoKey, loginFailedCountKey); } /** @@ -385,21 +395,16 @@ public class AuthenticationServiceImpl implements AuthenticationService { /** * 无效化其他登录信息 * - * @param id id - * @param loginTime loginTime - * @param remoteAddr remoteAddr - * @param location location - * @param userAgent userAgent + * @param id id + * @param identity identity */ @SuppressWarnings("ALL") - private void invalidOtherDeviceToken(Long id, long loginTime, - String remoteAddr, String location, String userAgent) { + private void invalidOtherDeviceToken(Long id, RequestIdentityModel identity) { // 获取登录信息 - Set loginKeyList = RedisUtils.scanKeys(UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*")); + Set loginKeyList = RedisStrings.scanKeys(UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*")); if (!loginKeyList.isEmpty()) { // 获取有效登录信息 - List loginTokenInfoList = redisTemplate.opsForValue() - .multiGet(loginKeyList) + List loginTokenInfoList = RedisStrings.getList(loginKeyList) .stream() .filter(Objects::nonNull) .map(s -> JSON.parseObject(s, LoginTokenDTO.class)) @@ -407,47 +412,45 @@ public class AuthenticationServiceImpl implements AuthenticationService { .collect(Collectors.toList()); // 修改登录信息 for (LoginTokenDTO loginTokenInfo : loginTokenInfoList) { - String deviceLoginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTokenInfo.getOrigin().getLoginTime()); + String deviceLoginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTokenInfo.getOrigin().getTimestamp()); loginTokenInfo.setStatus(LoginTokenStatusEnum.OTHER_DEVICE.getStatus()); - loginTokenInfo.setOverride(new LoginTokenIdentityDTO(loginTime, remoteAddr, location, userAgent)); + loginTokenInfo.setOverride(identity); RedisStrings.setJson(deviceLoginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginTokenInfo); } } // 删除续签信息 if (Booleans.isTrue(appLoginConfig.getAllowRefresh())) { - RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*")); + RedisStrings.scanKeysDelete(UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*")); } } /** * 生成 loginToken * - * @param user user - * @param loginTime loginTime - * @param remoteAddr remoteAddr - * @param location location - * @param userAgent userAgent + * @param user user + * @param identity identity * @return loginToken */ - private String generatorLoginToken(SystemUserDO user, long loginTime, - String remoteAddr, String location, String userAgent) { + private String generatorLoginToken(SystemUserDO user, RequestIdentityModel identity) { Long id = user.getId(); + Long timestamp = identity.getTimestamp(); // 生成 loginToken - String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTime); + String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, timestamp); LoginTokenDTO loginValue = LoginTokenDTO.builder() .id(id) + .username(user.getUsername()) .status(LoginTokenStatusEnum.OK.getStatus()) .refreshCount(0) - .origin(new LoginTokenIdentityDTO(loginTime, remoteAddr, location, userAgent)) + .origin(new RequestIdentityModel(timestamp, identity.getAddress(), identity.getLocation(), identity.getUserAgent())) .build(); RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginValue); // 生成 refreshToken if (Booleans.isTrue(appLoginConfig.getAllowRefresh())) { - String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, loginTime); + String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, timestamp); RedisStrings.setJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH, loginValue); } // 返回token - return AesEncryptUtils.encryptBase62(id + ":" + loginTime); + return AesEncryptUtils.encryptBase62(id + ":" + timestamp); } /** @@ -459,6 +462,10 @@ public class AuthenticationServiceImpl implements AuthenticationService { UserCacheKeyDefine.LOGIN_TOKEN.setTimeout(loginSessionTime); UserCacheKeyDefine.LOGIN_REFRESH.setTimeout(loginSessionTime + appLoginConfig.getRefreshInterval()); } + Integer loginFailedLockTime = appLoginConfig.getLoginFailedLockTime(); + if (loginFailedLockTime != null) { + UserCacheKeyDefine.LOGIN_FAILED.setTimeout(loginFailedLockTime); + } } } diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java index ffdc14c5..ef0da3b9 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java @@ -130,7 +130,7 @@ public class SystemUserServiceImpl implements SystemUserService { // 用户列表 UserCacheKeyDefine.USER_LIST.getKey(), // 登录失败次数 - UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername()) + UserCacheKeyDefine.LOGIN_FAILED.format(request.getUsername()) ); return record.getId(); } @@ -179,7 +179,7 @@ public class SystemUserServiceImpl implements SystemUserService { log.info("SystemUserService-updateUserStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord)); // 改为启用则删除登录失败次数缓存 if (UserStatusEnum.ENABLED.equals(status)) { - RedisUtils.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername())); + RedisUtils.delete(UserCacheKeyDefine.LOGIN_FAILED.format(record.getUsername())); } // 更新用户缓存中的 status RedisStrings.processSetJson(UserCacheKeyDefine.USER_INFO, s -> { @@ -320,7 +320,7 @@ public class SystemUserServiceImpl implements SystemUserService { int effect = systemUserDAO.updateById(update); log.info("SystemUserService-resetPassword record: {}, effect: {}", JSON.toJSONString(update), effect); // 删除登录失败次数缓存 - RedisUtils.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername())); + RedisUtils.delete(UserCacheKeyDefine.LOGIN_FAILED.format(record.getUsername())); // 删除登录缓存 RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*")); // 删除续签信息 @@ -375,7 +375,7 @@ public class SystemUserServiceImpl implements SystemUserService { // 用户信息缓存 deleteKeys.add(UserCacheKeyDefine.USER_INFO.format(id)); // 登录失败次数 - deleteKeys.add(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(s.getUsername())); + deleteKeys.add(UserCacheKeyDefine.LOGIN_FAILED.format(s.getUsername())); // 登录 token deleteKeys.addAll(RedisUtils.scanKeys(UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*"))); // 刷新 token