diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 63913572a..37a2c8a1b 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -172,11 +172,6 @@ ${revision} - - mysql - mysql-connector-java - ${mysql.version} - com.alibaba druid-spring-boot-starter diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java index c78148a46..20d567963 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java @@ -1,14 +1,12 @@ package cn.iocoder.yudao.framework.security.core; import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import java.util.*; +import java.util.HashMap; +import java.util.Map; /** * 登录用户信息 @@ -16,7 +14,7 @@ import java.util.*; * @author 芋道源码 */ @Data -public class LoginUser implements UserDetails { +public class LoginUser { /** * 用户编号 @@ -28,23 +26,6 @@ public class LoginUser implements UserDetails { * 关联 {@link UserTypeEnum} */ private Integer userType; - /** - * 最后更新时间 - */ - private Date updateTime; - - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 状态 - */ - private Integer status; /** * 租户编号 */ @@ -59,49 +40,6 @@ public class LoginUser implements UserDetails { @JsonIgnore private Map context; - @Override - @JsonIgnore// 避免序列化 - public String getPassword() { - return password; - } - - @Override - public String getUsername() { - return username; - } - - @Override - @JsonIgnore// 避免序列化 - public boolean isEnabled() { - return CommonStatusEnum.ENABLE.getStatus().equals(status); - } - - @Override - @JsonIgnore// 避免序列化 - public Collection getAuthorities() { - return new HashSet<>(); - } - - @Override - @JsonIgnore// 避免序列化 - public boolean isAccountNonExpired() { - return true; // 返回 true,不依赖 Spring Security 判断 - } - - @Override - @JsonIgnore// 避免序列化 - public boolean isAccountNonLocked() { - return true; // 返回 true,不依赖 Spring Security 判断 - } - - @Override - @JsonIgnore// 避免序列化 - public boolean isCredentialsNonExpired() { - return true; // 返回 true,不依赖 Spring Security 判断 - } - - // ========== 上下文 ========== - public void setContext(String key, Object value) { if (context == null) { context = new HashMap<>(); diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/SpringSecurityUser.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/SpringSecurityUser.java new file mode 100644 index 000000000..67b0c5ea6 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/SpringSecurityUser.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.framework.security.core.authentication; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Collections; + +/** + * 登录用户信息 + * + * @author 芋道源码 + */ +@Data +@AllArgsConstructor +public class SpringSecurityUser implements UserDetails { + + /** + * 用户编号 + */ + private Long id; + + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 状态 + */ + private Integer status; + /** + * 租户编号 + */ + private Long tenantId; + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public boolean isEnabled() { + return CommonStatusEnum.ENABLE.getStatus().equals(status); + } + + @Override + public Collection getAuthorities() { + return Collections.emptyList(); + } + + @Override + public boolean isAccountNonExpired() { + return true; // 返回 true,不依赖 Spring Security 判断 + } + + @Override + public boolean isAccountNonLocked() { + return true; // 返回 true,不依赖 Spring Security 判断 + } + + @Override + public boolean isCredentialsNonExpired() { + return true; // 返回 true,不依赖 Spring Security 判断 + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java index bbc1a9435..253974539 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java @@ -11,6 +11,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; +import java.util.Collections; /** * 安全服务工具类 @@ -98,7 +99,7 @@ public class SecurityFrameworkUtils { private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { // 创建 UsernamePasswordAuthenticationToken 对象 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - loginUser, null, loginUser.getAuthorities()); + loginUser, null, Collections.emptyList()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); return authenticationToken; } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java index a15555df1..f33f85e70 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.member.convert.auth; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser; import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; @@ -19,13 +19,12 @@ public interface AuthConvert { AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); - @Mapping(source = "mobile", target = "username") - LoginUser convert0(MemberUserDO bean); + LoginUser convert(MemberUserDO bean); - default LoginUser convert(MemberUserDO bean) { - // 目的,为了设置 UserTypeEnum.MEMBER.getValue() - return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue()); - } + @Mapping(source = "mobile", target = "username") + SpringSecurityUser convert2(MemberUserDO user); + + LoginUser convert(SpringSecurityUser bean); SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO); SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO); diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java index f82028f39..b79e55830 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.member.service.auth; import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; @@ -78,7 +77,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { throw new UsernameNotFoundException(mobile); } // 创建 LoginUser 对象 - return AuthConvert.INSTANCE.convert(user); + return AuthConvert.INSTANCE.convert2(user); } @Override @@ -87,7 +86,8 @@ public class MemberAuthServiceImpl implements MemberAuthService { LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword()); // 缓存登录用户到 Redis 中,返回 Token 令牌 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), + LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); } @Override @@ -101,10 +101,11 @@ public class MemberAuthServiceImpl implements MemberAuthService { Assert.notNull(user, "获取用户失败,结果为空"); // 执行登陆 - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); + LoginUser loginUser = buildLoginUser(user); // 缓存登录用户到 Redis 中,返回 Token 令牌 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), + LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); } @Override @@ -123,10 +124,11 @@ public class MemberAuthServiceImpl implements MemberAuthService { } // 创建 LoginUser 对象 - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); + LoginUser loginUser = buildLoginUser(user); // 缓存登录用户到 Redis 中,返回 Token 令牌 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, null, + LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); } @Override @@ -142,9 +144,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { return token; } - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { + private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String mobile, + LoginLogTypeEnum logType, String userIp, String userAgent) { // 插入登陆日志 - createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); + createLoginLog(loginUser.getId(), mobile, logType, LoginResultEnum.SUCCESS); // 缓存登录用户到 Redis 中,返回 Token 令牌 return userSessionApi.createUserSession(loginUser, userIp, userAgent); } @@ -164,29 +167,32 @@ public class MemberAuthServiceImpl implements MemberAuthService { authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( username, password, getUserType())); } catch (BadCredentialsException badCredentialsException) { - this.createLoginLog(username, logType, LoginResultEnum.BAD_CREDENTIALS); + this.createLoginLog(null, username, logType, LoginResultEnum.BAD_CREDENTIALS); throw exception(AUTH_LOGIN_BAD_CREDENTIALS); } catch (DisabledException disabledException) { - this.createLoginLog(username, logType, LoginResultEnum.USER_DISABLED); + this.createLoginLog(null, username, logType, LoginResultEnum.USER_DISABLED); throw exception(AUTH_LOGIN_USER_DISABLED); } catch (AuthenticationException authenticationException) { log.error("[login0][username({}) 发生未知异常]", username, authenticationException); - this.createLoginLog(username, logType, LoginResultEnum.UNKNOWN_ERROR); + this.createLoginLog(null, username, logType, LoginResultEnum.UNKNOWN_ERROR); throw exception(AUTH_LOGIN_FAIL_UNKNOWN); } Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); return (LoginUser) authentication.getPrincipal(); } - private void createLoginLog(String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { + private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { // 获得用户 - MemberUserDO user = userService.getUserByMobile(mobile); + if (userId == null) { + MemberUserDO user = userService.getUserByMobile(mobile); + userId = user != null ? user.getId() : null; + } // 插入登录日志 LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); reqDTO.setLogType(logType.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); - if (user != null) { - reqDTO.setUserId(user.getId()); + if (userId != null) { + reqDTO.setUserId(userId); } reqDTO.setUserType(getUserType().getValue()); reqDTO.setUsername(mobile); @@ -195,39 +201,14 @@ public class MemberAuthServiceImpl implements MemberAuthService { reqDTO.setResult(loginResult.getResult()); loginLogApi.createLoginLog(reqDTO); // 更新最后登录时间 - if (user != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { - userService.updateUserLogin(user.getId(), getClientIP()); + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { + userService.updateUserLogin(userId, getClientIP()); } } @Override public LoginUser verifyTokenAndRefresh(String token) { - // 获得 LoginUser - LoginUser loginUser = userSessionApi.getLoginUser(token); - if (loginUser == null) { - return null; - } - // 刷新 LoginUser 缓存 - this.refreshLoginUserCache(token, loginUser); - return loginUser; - } - - private void refreshLoginUserCache(String token, LoginUser loginUser) { - // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 - if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < - userSessionApi.getSessionTimeoutMillis() / 3) { - return; - } - - // 重新加载 UserDO 信息 - MemberUserDO user = userService.getUser(loginUser.getId()); - if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { - // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 - throw exception(AUTH_TOKEN_EXPIRED); - } - - // 刷新 LoginUser 缓存 - userSessionApi.refreshUserSession(token, loginUser); + return userSessionApi.getLoginUser(token); } @Override @@ -239,10 +220,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { } // 执行登陆 - this.createLoginLog(user.getMobile(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); + createLoginLog(userId, user.getMobile(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); // 创建 LoginUser 对象 - return AuthConvert.INSTANCE.convert(user); + return buildLoginUser(user); } @Override @@ -255,7 +236,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { // 删除 session userSessionApi.deleteUserSession(token); // 记录登出日志 - this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); + createLogoutLog(loginUser.getId()); } @Override @@ -321,17 +302,29 @@ public class MemberAuthServiceImpl implements MemberAuthService { return user; } - private void createLogoutLog(Long userId, String username) { + private void createLogoutLog(Long userId) { LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setUserId(userId); reqDTO.setUserType(getUserType().getValue()); - reqDTO.setUsername(username); + reqDTO.setUsername(getMobile(userId)); reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserIp(getClientIP()); reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); loginLogApi.createLoginLog(reqDTO); } + private LoginUser buildLoginUser(MemberUserDO user) { + return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue()); + } + + private String getMobile(Long userId) { + if (userId == null) { + return null; + } + MemberUserDO user = userService.getUser(userId); + return user != null ? user.getMobile() : null; + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index d7979bc81..77232d397 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -1,20 +1,17 @@ package cn.iocoder.yudao.module.system.convert.auth; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.permission.MenuIdEnum; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; import org.slf4j.LoggerFactory; @@ -25,13 +22,11 @@ public interface AuthConvert { AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); - @Mapping(source = "updateTime", target = "updateTime", ignore = true) // 字段相同,但是含义不同,忽略 - LoginUser convert0(AdminUserDO bean); + LoginUser convert(AdminUserDO bean); - default LoginUser convert(AdminUserDO bean) { - // 目的,为了设置 UserTypeEnum.ADMIN.getValue() - return convert0(bean).setUserType(UserTypeEnum.ADMIN.getValue()); - } + SpringSecurityUser convert2(AdminUserDO user); + + LoginUser convert(SpringSecurityUser bean); default AuthPermissionInfoRespVO convert(AdminUserDO user, List roleList, List menuList) { return AuthPermissionInfoRespVO.builder() diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index ebbfb6dfa..ab8cc9658 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.system.service.auth; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; +import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; @@ -79,7 +79,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { throw new UsernameNotFoundException(username); } // 创建 LoginUser 对象 - return buildLoginUser(user); + return AuthConvert.INSTANCE.convert2(user); } @Override @@ -89,7 +89,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { if (user == null) { throw new UsernameNotFoundException(String.valueOf(userId)); } - createLoginLog(user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); + createLoginLog(userId, user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); // 创建 LoginUser 对象 return buildLoginUser(user); @@ -104,7 +104,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); // 缓存登陆用户到 Redis 中,返回 Token 令牌 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(), + LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); } @Override @@ -132,7 +133,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { LoginUser loginUser = buildLoginUser(user); // 缓存登陆用户到 Redis 中,返回 sessionId 编号 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), + LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); } private void verifyCaptcha(AuthLoginReqVO reqVO) { @@ -147,13 +149,13 @@ public class AdminAuthServiceImpl implements AdminAuthService { String code = captchaService.getCaptchaCode(reqVO.getUuid()); if (code == null) { // 创建登录失败日志(验证码不存在) - this.createLoginLog(reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND); + createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND); throw exception(AUTH_LOGIN_CAPTCHA_NOT_FOUND); } // 验证码不正确 if (!code.equals(reqVO.getCode())) { // 创建登录失败日志(验证码不正确) - this.createLoginLog(reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR); + createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR); throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR); } // 正确,所以要删除下验证码 @@ -170,29 +172,35 @@ public class AdminAuthServiceImpl implements AdminAuthService { authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( username, password, getUserType())); } catch (BadCredentialsException badCredentialsException) { - this.createLoginLog(username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); throw exception(AUTH_LOGIN_BAD_CREDENTIALS); } catch (DisabledException disabledException) { - this.createLoginLog(username, logTypeEnum, LoginResultEnum.USER_DISABLED); + createLoginLog(null, username, logTypeEnum, LoginResultEnum.USER_DISABLED); throw exception(AUTH_LOGIN_USER_DISABLED); } catch (AuthenticationException authenticationException) { log.error("[login0][username({}) 发生未知异常]", username, authenticationException); - this.createLoginLog(username, logTypeEnum, LoginResultEnum.UNKNOWN_ERROR); + createLoginLog(null, username, logTypeEnum, LoginResultEnum.UNKNOWN_ERROR); throw exception(AUTH_LOGIN_FAIL_UNKNOWN); } Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); - return (LoginUser) authentication.getPrincipal(); + // 构建 User 对象 + return AuthConvert.INSTANCE.convert((SpringSecurityUser) authentication.getPrincipal()) + .setUserType(getUserType().getValue()); } - private void createLoginLog(String username, LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { + private void createLoginLog(Long userId, String username, + LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { // 获得用户 - AdminUserDO user = userService.getUserByUsername(username); + if (userId == null) { + AdminUserDO user = userService.getUserByUsername(username); + userId = user != null ? user.getId() : null; + } // 插入登录日志 LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); reqDTO.setLogType(logTypeEnum.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); - if (user != null) { - reqDTO.setUserId(user.getId()); + if (userId != null) { + reqDTO.setUserId(userId); } reqDTO.setUserType(getUserType().getValue()); reqDTO.setUsername(username); @@ -201,8 +209,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { reqDTO.setResult(loginResult.getResult()); loginLogService.createLoginLog(reqDTO); // 更新最后登录时间 - if (user != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { - userService.updateUserLogin(user.getId(), ServletUtils.getClientIP()); + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { + userService.updateUserLogin(userId, ServletUtils.getClientIP()); } } @@ -225,7 +233,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { LoginUser loginUser = buildLoginUser(user); // 缓存登录用户到 Redis 中,返回 Token 令牌 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, null, + LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); } @Override @@ -237,12 +246,14 @@ public class AdminAuthServiceImpl implements AdminAuthService { socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); // 缓存登录用户到 Redis 中,返回 Token 令牌 - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(), + LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); } - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { + private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String username, + LoginLogTypeEnum logType, String userIp, String userAgent) { // 插入登陆日志 - createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); + createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS); // 缓存登录用户到 Redis 中,返回 Token 令牌 return userSessionService.createUserSession(loginUser, userIp, userAgent); } @@ -257,7 +268,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { // 删除 session userSessionService.deleteUserSession(token); // 记录登出日志 - createLogoutLog(loginUser.getId(), loginUser.getUsername()); + createLogoutLog(loginUser.getId()); } @Override @@ -265,13 +276,13 @@ public class AdminAuthServiceImpl implements AdminAuthService { return UserTypeEnum.ADMIN; } - private void createLogoutLog(Long userId, String username) { + private void createLogoutLog(Long userId) { LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setUserId(userId); + reqDTO.setUsername(getUsername(userId)); reqDTO.setUserType(getUserType().getValue()); - reqDTO.setUsername(username); reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserIp(ServletUtils.getClientIP()); reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); @@ -280,36 +291,19 @@ public class AdminAuthServiceImpl implements AdminAuthService { @Override public LoginUser verifyTokenAndRefresh(String token) { - // 获得 LoginUser - LoginUser loginUser = userSessionService.getLoginUser(token); - if (loginUser == null) { - return null; - } - // 刷新 LoginUser 缓存 - return this.refreshLoginUserCache(token, loginUser); - } - - private LoginUser refreshLoginUserCache(String token, LoginUser loginUser) { - // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 - if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < - userSessionService.getSessionTimeoutMillis() / 3) { - return loginUser; - } - - // 重新加载 AdminUserDO 信息 - AdminUserDO user = userService.getUser(loginUser.getId()); - if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { - throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 - } - - // 刷新 LoginUser 缓存 - LoginUser newLoginUser= buildLoginUser(user); - userSessionService.refreshUserSession(token, newLoginUser); - return newLoginUser; + return userSessionService.getLoginUser(token); } private LoginUser buildLoginUser(AdminUserDO user) { - return AuthConvert.INSTANCE.convert(user); + return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue()); + } + + private String getUsername(Long userId) { + if (userId == null) { + return null; + } + AdminUserDO user = userService.getUser(userId); + return user != null ? user.getUsername() : null; } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java index 333e54c69..75cedcf18 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java @@ -23,7 +23,6 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.Duration; import java.util.Collection; -import java.util.Date; import java.util.List; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; @@ -106,12 +105,11 @@ public class UserSessionServiceImpl implements UserSessionService { // 生成 Session 编号 String token = generateToken(); // 写入 Redis 缓存 - loginUser.setUpdateTime(new Date()); loginUserRedisDAO.set(token, loginUser); // 写入 DB 中 UserSessionDO userSession = UserSessionDO.builder().token(token) .userId(loginUser.getId()).userType(loginUser.getUserType()) - .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) + .userIp(userIp).userAgent(userAgent).username("") .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) .build(); userSessionMapper.insert(userSession); @@ -121,15 +119,7 @@ public class UserSessionServiceImpl implements UserSessionService { @Override public void refreshUserSession(String token, LoginUser loginUser) { - // 写入 Redis 缓存 - loginUser.setUpdateTime(new Date()); - loginUserRedisDAO.set(token, loginUser); - // 更新 DB 中 - UserSessionDO updateObj = UserSessionDO.builder().build(); - updateObj.setUsername(loginUser.getUsername()); - updateObj.setUpdateTime(new Date()); - updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); - userSessionMapper.updateByToken(token, updateObj); + } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java index ab739be79..e06591cca 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java @@ -201,7 +201,7 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { assertPojoEquals(redisLoginUser, loginUser, "username", "password"); // 校验 UserSessionDO 记录 UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); - assertEquals(updateDO.getUsername(), loginUser.getUsername()); +// assertEquals(updateDO.getUsername(), loginUser.getUsername()); assertNotNull(userSession.getUpdateTime()); assertNotNull(userSession.getSessionTimeout()); }