Commit 3bd7e8e6829300d9f1d059b5b50123df3fc67af4

Authored by YunaiV
1 parent 3351ebbb

去除 Spring Security 的 Admin 的 loadUsername,使用自己定义的 login0 实现

yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java
... ... @@ -105,16 +105,6 @@ public class MultiUserDetailsAuthenticationProvider extends AbstractUserDetailsA
105 105 return selectService(request).verifyTokenAndRefresh(token);
106 106 }
107 107  
108   - /**
109   - * 基于 token 退出登录
110   - *
111   - * @param request 请求
112   - * @param token token
113   - */
114   - public void logout(HttpServletRequest request, String token) {
115   - selectService(request).logout(token);
116   - }
117   -
118 108 private SecurityAuthFrameworkService selectService(HttpServletRequest request) {
119 109 // 第一步,获得用户类型
120 110 UserTypeEnum userType = getUserType(request);
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java
... ... @@ -21,13 +21,6 @@ public interface SecurityAuthFrameworkService extends UserDetailsService {
21 21 LoginUser verifyTokenAndRefresh(String token);
22 22  
23 23 /**
24   - * 基于 token 退出登录
25   - *
26   - * @param token token
27   - */
28   - void logout(String token);
29   -
30   - /**
31 24 * 获得用户类型。每个用户类型,对应一个 SecurityAuthFrameworkService 实现类。
32 25 *
33 26 * @return 用户类型
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
... ... @@ -25,6 +25,13 @@ public interface MemberAuthService extends SecurityAuthFrameworkService {
25 25 String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent);
26 26  
27 27 /**
  28 + * 基于 token 退出登录
  29 + *
  30 + * @param token token
  31 + */
  32 + void logout(String token);
  33 +
  34 + /**
28 35 * 手机 + 验证码登陆
29 36 *
30 37 * @param reqVO 登陆信息
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
... ... @@ -12,7 +12,6 @@ public interface ErrorCodeConstants {
12 12 // ========== AUTH 模块 1002000000 ==========
13 13 ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确");
14 14 ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用");
15   - ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1002000002, "登录失败"); // 登录失败的兜底,未知原因
16 15 ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在");
17 16 ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确");
18 17 ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定");
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java
... ... @@ -26,8 +26,6 @@ public interface AuthConvert {
26 26  
27 27 SpringSecurityUser convert2(AdminUserDO user);
28 28  
29   - LoginUser convert(SpringSecurityUser bean);
30   -
31 29 default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
32 30 return AuthPermissionInfoRespVO.builder()
33 31 .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build())
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
1 1 package cn.iocoder.yudao.module.system.service.auth;
2 2  
3   -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
4 3 import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
  4 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
5 5  
6 6 import javax.validation.Valid;
7 7  
8 8 /**
9 9 * 管理后台的认证 Service 接口
10 10 *
11   - * 提供用户的账号密码登录、token 的校验等认证相关的功能
  11 + * 提供用户的登录、登出的能力
12 12 *
13 13 * @author 芋道源码
14 14 */
... ... @@ -25,6 +25,13 @@ public interface AdminAuthService extends SecurityAuthFrameworkService {
25 25 String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent);
26 26  
27 27 /**
  28 + * 基于 token 退出登录
  29 + *
  30 + * @param token token
  31 + */
  32 + void logout(String token);
  33 +
  34 + /**
28 35 * 短信验证码发送
29 36 *
30 37 * @param reqVO 发送请求
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.auth;
2 2  
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 5 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 6 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
5 7 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
6 8 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
7 9 import cn.iocoder.yudao.framework.security.core.LoginUser;
8   -import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
9   -import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser;
10 10 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
11 11 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
12 12 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
... ... @@ -19,18 +19,11 @@ import cn.iocoder.yudao.module.system.service.common.CaptchaService;
19 19 import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
20 20 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
21 21 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  22 +import com.google.common.annotations.VisibleForTesting;
22 23 import lombok.extern.slf4j.Slf4j;
23   -import org.springframework.beans.factory.annotation.Autowired;
24   -import org.springframework.context.annotation.Lazy;
25   -import org.springframework.security.authentication.AuthenticationManager;
26   -import org.springframework.security.authentication.BadCredentialsException;
27   -import org.springframework.security.authentication.DisabledException;
28   -import org.springframework.security.core.Authentication;
29   -import org.springframework.security.core.AuthenticationException;
30 24 import org.springframework.security.core.userdetails.UserDetails;
31 25 import org.springframework.security.core.userdetails.UsernameNotFoundException;
32 26 import org.springframework.stereotype.Service;
33   -import org.springframework.util.Assert;
34 27  
35 28 import javax.annotation.Resource;
36 29 import javax.validation.Validator;
... ... @@ -50,11 +43,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
50 43 public class AdminAuthServiceImpl implements AdminAuthService {
51 44  
52 45 @Resource
53   - @Lazy // 延迟加载,因为存在相互依赖的问题
54   - private AuthenticationManager authenticationManager;
55   -
56   - @Autowired
57   - @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // UserService 存在重名
58 46 private AdminUserService userService;
59 47 @Resource
60 48 private CaptchaService captchaService;
... ... @@ -72,17 +60,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
72 60 private SmsCodeApi smsCodeApi;
73 61  
74 62 @Override
75   - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
76   - // 获取 username 对应的 AdminUserDO
77   - AdminUserDO user = userService.getUserByUsername(username);
78   - if (user == null) {
79   - throw new UsernameNotFoundException(username);
80   - }
81   - // 创建 LoginUser 对象
82   - return AuthConvert.INSTANCE.convert2(user);
83   - }
84   -
85   - @Override
86 63 public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
87 64 // 判断验证码是否正确
88 65 verifyCaptcha(reqVO);
... ... @@ -124,7 +101,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
124 101 LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent);
125 102 }
126 103  
127   - private void verifyCaptcha(AuthLoginReqVO reqVO) {
  104 + @VisibleForTesting
  105 + void verifyCaptcha(AuthLoginReqVO reqVO) {
128 106 // 如果验证码关闭,则不进行校验
129 107 if (!captchaService.isCaptchaEnable()) {
130 108 return;
... ... @@ -149,46 +127,36 @@ public class AdminAuthServiceImpl implements AdminAuthService {
149 127 captchaService.deleteCaptchaCode(reqVO.getUuid());
150 128 }
151 129  
152   - private LoginUser login0(String username, String password) {
  130 + @VisibleForTesting
  131 + LoginUser login0(String username, String password) {
153 132 final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
154   - // 用户验证
155   - Authentication authentication;
156   - try {
157   - // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证
158   - // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息
159   - authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken(
160   - username, password, getUserType()));
161   - } catch (BadCredentialsException badCredentialsException) {
  133 + // 校验账号是否存在
  134 + AdminUserDO user = userService.getUserByUsername(username);
  135 + if (user == null) {
162 136 createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
163 137 throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
164   - } catch (DisabledException disabledException) {
165   - createLoginLog(null, username, logTypeEnum, LoginResultEnum.USER_DISABLED);
  138 + }
  139 + if (!userService.isPasswordMatch(password, user.getPassword())) {
  140 + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
  141 + throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
  142 + }
  143 + // 校验是否禁用
  144 + if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
  145 + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
166 146 throw exception(AUTH_LOGIN_USER_DISABLED);
167   - } catch (AuthenticationException authenticationException) {
168   - log.error("[login0][username({}) 发生未知异常]", username, authenticationException);
169   - createLoginLog(null, username, logTypeEnum, LoginResultEnum.UNKNOWN_ERROR);
170   - throw exception(AUTH_LOGIN_FAIL_UNKNOWN);
171 147 }
172   - Assert.notNull(authentication.getPrincipal(), "Principal 不会为空");
  148 +
173 149 // 构建 User 对象
174   - return AuthConvert.INSTANCE.convert((SpringSecurityUser) authentication.getPrincipal())
175   - .setUserType(getUserType().getValue());
  150 + return buildLoginUser(user);
176 151 }
177 152  
178 153 private void createLoginLog(Long userId, String username,
179 154 LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
180   - // 获得用户
181   - if (userId == null) {
182   - AdminUserDO user = userService.getUserByUsername(username);
183   - userId = user != null ? user.getId() : null;
184   - }
185 155 // 插入登录日志
186 156 LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
187 157 reqDTO.setLogType(logTypeEnum.getType());
188 158 reqDTO.setTraceId(TracerUtils.getTraceId());
189   - if (userId != null) {
190   - reqDTO.setUserId(userId);
191   - }
  159 + reqDTO.setUserId(userId);
192 160 reqDTO.setUserType(getUserType().getValue());
193 161 reqDTO.setUsername(username);
194 162 reqDTO.setUserAgent(ServletUtils.getUserAgent());
... ... @@ -293,4 +261,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
293 261 return user != null ? user.getUsername() : null;
294 262 }
295 263  
  264 + @Override
  265 + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  266 + return null;
  267 + }
296 268 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
... ... @@ -105,7 +105,6 @@ public interface AdminUserService {
105 105 */
106 106 AdminUserDO getUserByMobile(String mobile);
107 107  
108   -
109 108 /**
110 109 * 获得用户分页列表
111 110 *
... ... @@ -209,4 +208,13 @@ public interface AdminUserService {
209 208 */
210 209 List<AdminUserDO> getUsersByStatus(Integer status);
211 210  
  211 + /**
  212 + * 判断密码是否匹配
  213 + *
  214 + * @param rawPassword 未加密的密码
  215 + * @param encodedPassword 加密后的密码
  216 + * @return 是否匹配
  217 + */
  218 + boolean isPasswordMatch(String rawPassword, String encodedPassword);
  219 +
212 220 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
... ... @@ -148,7 +148,7 @@ public class AdminUserServiceImpl implements AdminUserService {
148 148 checkOldPassword(id, reqVO.getOldPassword());
149 149 // 执行更新
150 150 AdminUserDO updateObj = new AdminUserDO().setId(id);
151   - updateObj.setPassword(passwordEncoder.encode(reqVO.getNewPassword())); // 加密密码
  151 + updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码
152 152 userMapper.updateById(updateObj);
153 153 }
154 154  
... ... @@ -172,7 +172,7 @@ public class AdminUserServiceImpl implements AdminUserService {
172 172 // 更新密码
173 173 AdminUserDO updateObj = new AdminUserDO();
174 174 updateObj.setId(id);
175   - updateObj.setPassword(passwordEncoder.encode(password)); // 加密密码
  175 + updateObj.setPassword(encodePassword(password)); // 加密密码
176 176 userMapper.updateById(updateObj);
177 177 }
178 178  
... ... @@ -205,11 +205,6 @@ public class AdminUserServiceImpl implements AdminUserService {
205 205 return userMapper.selectByUsername(username);
206 206 }
207 207  
208   - /**
209   - * 通过手机号获取用户
210   - * @param mobile
211   - * @return
212   - */
213 208 @Override
214 209 public AdminUserDO getUserByMobile(String mobile) {
215 210 return userMapper.selectByMobile(mobile);
... ... @@ -395,7 +390,7 @@ public class AdminUserServiceImpl implements AdminUserService {
395 390 if (user == null) {
396 391 throw exception(USER_NOT_EXISTS);
397 392 }
398   - if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
  393 + if (!isPasswordMatch(oldPassword, user.getPassword())) {
399 394 throw exception(USER_PASSWORD_FAILED);
400 395 }
401 396 }
... ... @@ -421,7 +416,7 @@ public class AdminUserServiceImpl implements AdminUserService {
421 416 AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
422 417 if (existUser == null) {
423 418 userMapper.insert(UserConvert.INSTANCE.convert(importUser)
424   - .setPassword(passwordEncoder.encode(userInitPassword))); // 设置默认密码
  419 + .setPassword(encodePassword(userInitPassword))); // 设置默认密码
425 420 respVO.getCreateUsernames().add(importUser.getUsername());
426 421 return;
427 422 }
... ... @@ -443,4 +438,19 @@ public class AdminUserServiceImpl implements AdminUserService {
443 438 return userMapper.selectListByStatus(status);
444 439 }
445 440  
  441 + @Override
  442 + public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
  443 + return passwordEncoder.matches(rawPassword, encodedPassword);
  444 + }
  445 +
  446 + /**
  447 + * 对密码进行加密
  448 + *
  449 + * @param password 密码
  450 + * @return 加密后的密码
  451 + */
  452 + private String encodePassword(String password) {
  453 + return passwordEncoder.encode(password);
  454 + }
  455 +
446 456 }
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
1 1 package cn.iocoder.yudao.module.system.service.auth;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.security.core.LoginUser;
4 5 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
5 6 import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
... ... @@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
9 10 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
10 11 import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
11 12 import cn.iocoder.yudao.module.system.service.common.CaptchaService;
12   -import cn.iocoder.yudao.module.system.service.dept.PostService;
13 13 import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
14 14 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
15 15 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
... ... @@ -17,23 +17,16 @@ import org.junit.jupiter.api.BeforeEach;
17 17 import org.junit.jupiter.api.Test;
18 18 import org.springframework.boot.test.mock.mockito.MockBean;
19 19 import org.springframework.context.annotation.Import;
20   -import org.springframework.security.authentication.AuthenticationManager;
21   -import org.springframework.security.authentication.BadCredentialsException;
22   -import org.springframework.security.authentication.DisabledException;
23   -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
24   -import org.springframework.security.core.Authentication;
25   -import org.springframework.security.core.AuthenticationException;
26   -import org.springframework.security.core.userdetails.UsernameNotFoundException;
27 20  
28 21 import javax.annotation.Resource;
29 22 import javax.validation.Validator;
30   -import java.util.Set;
31 23  
  24 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
32 25 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
33   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  26 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  27 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
34 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
35 29 import static org.junit.jupiter.api.Assertions.assertEquals;
36   -import static org.junit.jupiter.api.Assertions.assertThrows;
37 30 import static org.mockito.ArgumentMatchers.eq;
38 31 import static org.mockito.Mockito.*;
39 32  
... ... @@ -46,10 +39,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
46 39 @MockBean
47 40 private AdminUserService userService;
48 41 @MockBean
49   - private AuthenticationManager authenticationManager;
50   - @MockBean
51   - private Authentication authentication;
52   - @MockBean
53 42 private CaptchaService captchaService;
54 43 @MockBean
55 44 private LoginLogService loginLogService;
... ... @@ -58,8 +47,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
58 47 @MockBean
59 48 private SocialUserService socialService;
60 49 @MockBean
61   - private PostService postService;
62   - @MockBean
63 50 private SmsCodeApi smsCodeApi;
64 51  
65 52 @MockBean
... ... @@ -71,131 +58,124 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
71 58 }
72 59  
73 60 @Test
74   - public void testLoadUserByUsername_success() {
  61 + public void testLogin0_success() {
75 62 // 准备参数
76 63 String username = randomString();
77   - // mock 方法
78   - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username));
  64 + String password = randomString();
  65 + // mock user 数据
  66 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
  67 + .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
79 68 when(userService.getUserByUsername(eq(username))).thenReturn(user);
  69 + // mock password 匹配
  70 + when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
80 71  
81 72 // 调用
82   - LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username);
  73 + LoginUser loginUser = authService.login0(username, password);
83 74 // 校验
84   - AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
  75 + assertPojoEquals(user, loginUser);
85 76 }
86 77  
87 78 @Test
88   - public void testLoadUserByUsername_userNotFound() {
  79 + public void testLogin0_userNotFound() {
89 80 // 准备参数
90 81 String username = randomString();
91   - // mock 方法
  82 + String password = randomString();
92 83  
93 84 // 调用, 并断言异常
94   - assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
95   - () -> authService.loadUserByUsername(username),
96   - username); // 异常提示为 username
  85 + AssertUtils.assertServiceException(() -> authService.login0(username, password),
  86 + AUTH_LOGIN_BAD_CREDENTIALS);
  87 + verify(loginLogService).createLoginLog(
  88 + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
  89 + && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
  90 + && o.getUserId() == null)
  91 + );
97 92 }
98 93  
99 94 @Test
100   - public void testLogin_captchaNotFound() {
  95 + public void testLogin0_badCredentials() {
101 96 // 准备参数
102   - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
103   - String userIp = randomString();
104   - String userAgent = randomString();
  97 + String username = randomString();
  98 + String password = randomString();
  99 + // mock user 数据
  100 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
  101 + .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
  102 + when(userService.getUserByUsername(eq(username))).thenReturn(user);
105 103  
106 104 // 调用, 并断言异常
107   - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
108   - // 校验调用参数
109   - verify(loginLogService, times(1)).createLoginLog(
110   - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
111   - && o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
  105 + AssertUtils.assertServiceException(() -> authService.login0(username, password),
  106 + AUTH_LOGIN_BAD_CREDENTIALS);
  107 + verify(loginLogService).createLoginLog(
  108 + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
  109 + && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
  110 + && o.getUserId().equals(user.getId()))
112 111 );
113 112 }
114 113  
115 114 @Test
116   - public void testLogin_captchaCodeError() {
  115 + public void testLogin0_userDisabled() {
117 116 // 准备参数
118   - String userIp = randomString();
119   - String userAgent = randomString();
120   - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
121   -
122   - // mock 验证码不正确
123   - String code = randomString();
124   - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
  117 + String username = randomString();
  118 + String password = randomString();
  119 + // mock user 数据
  120 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
  121 + .setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus()));
  122 + when(userService.getUserByUsername(eq(username))).thenReturn(user);
  123 + // mock password 匹配
  124 + when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
125 125  
126 126 // 调用, 并断言异常
127   - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
128   - // 校验调用参数
129   - verify(loginLogService, times(1)).createLoginLog(
130   - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
131   - && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
  127 + AssertUtils.assertServiceException(() -> authService.login0(username, password),
  128 + AUTH_LOGIN_USER_DISABLED);
  129 + verify(loginLogService).createLoginLog(
  130 + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
  131 + && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
  132 + && o.getUserId().equals(user.getId()))
132 133 );
133 134 }
134 135  
135 136 @Test
136   - public void testLogin_badCredentials() {
  137 + public void testCaptcha_success() {
137 138 // 准备参数
138   - String userIp = randomString();
139   - String userAgent = randomString();
140 139 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
  140 +
141 141 // mock 验证码正确
142 142 when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
143   - // mock 抛出异常
144   - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
145   - .thenThrow(new BadCredentialsException("测试账号或密码不正确"));
146 143  
147   - // 调用, 并断言异常
148   - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS);
149   - // 校验调用参数
150   - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
151   - verify(loginLogService, times(1)).createLoginLog(
152   - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
153   - && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()))
154   - );
  144 + // 调用
  145 + authService.verifyCaptcha(reqVO);
  146 + // 断言
  147 + verify(captchaService).deleteCaptchaCode(reqVO.getUuid());
155 148 }
156 149  
157 150 @Test
158   - public void testLogin_userDisabled() {
  151 + public void testCaptcha_notFound() {
159 152 // 准备参数
160   - String userIp = randomString();
161   - String userAgent = randomString();
162 153 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
163 154  
164   - // mock 验证码正确
165   - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
166   - // mock 抛出异常
167   - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
168   - .thenThrow(new DisabledException("测试用户被禁用"));
169   -
170 155 // 调用, 并断言异常
171   - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED);
  156 + assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
172 157 // 校验调用参数
173   - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
174 158 verify(loginLogService, times(1)).createLoginLog(
175 159 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
176   - && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult()))
  160 + && o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
177 161 );
178 162 }
179 163  
180 164 @Test
181   - public void testLogin_unknownError() {
  165 + public void testCaptcha_codeError() {
182 166 // 准备参数
183   - String userIp = randomString();
184   - String userAgent = randomString();
185 167 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
186   - // mock 验证码正确
187   - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
188   - // mock 抛出异常
189   - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
190   - .thenThrow(new AuthenticationException("测试未知异常") {});
  168 +
  169 + // mock 验证码不正确
  170 + String code = randomString();
  171 + when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
191 172  
192 173 // 调用, 并断言异常
193   - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN);
  174 + assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
194 175 // 校验调用参数
195   - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
196   - verify(loginLogService, times(1)).createLoginLog(
  176 + verify(loginLogService).createLoginLog(
197 177 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
198   - && o.getResult().equals(LoginResultEnum.UNKNOWN_ERROR.getResult()))
  178 + && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
199 179 );
200 180 }
201 181  
... ... @@ -204,29 +184,32 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
204 184 // 准备参数
205 185 String userIp = randomString();
206 186 String userAgent = randomString();
207   - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
  187 + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
  188 + o.setUsername("test_username").setPassword("test_password"));
208 189  
209 190 // mock 验证码正确
210 191 when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
211   - // mock authentication
212   - Long userId = randomLongId();
213   - Set<Long> userRoleIds = randomSet(Long.class);
214   - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(userId));
215   - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
216   - .thenReturn(authentication);
217   - when(authentication.getPrincipal()).thenReturn(loginUser);
  192 + // mock user 数据
  193 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername("test_username")
  194 + .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
  195 + when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
  196 + // mock password 匹配
  197 + when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
218 198 // mock 缓存登录用户到 Redis
219 199 String token = randomString();
220   - when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(token);
  200 + when(userSessionService.createUserSession(argThat(argument -> {
  201 + AssertUtils.assertPojoEquals(user, argument);
  202 + return true;
  203 + }), eq(userIp), eq(userAgent))).thenReturn(token);
221 204  
222 205 // 调用, 并断言异常
223   - String login = authService.login(reqVO, userIp, userAgent);
224   - assertEquals(token, login);
  206 + String result = authService.login(reqVO, userIp, userAgent);
  207 + assertEquals(token, result);
225 208 // 校验调用参数
226   - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
227   - verify(loginLogService, times(1)).createLoginLog(
  209 + verify(loginLogService).createLoginLog(
228 210 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
229   - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
  211 + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
  212 + && o.getUserId().equals(user.getId()))
230 213 );
231 214 }
232 215  
... ...