Commit 8e0569ee544b26cc6944c7eab6bb92d82bca57f6

Authored by 宋天
1 parent c84e19e1

update 新增接口:修改手机,修改密码,忘记密码

Showing 19 changed files with 696 additions and 18 deletions
yudao-user-server/pom.xml
... ... @@ -91,6 +91,13 @@
91 91 <scope>test</scope>
92 92 </dependency>
93 93  
  94 + <!--单元测试相关-->
  95 + <dependency>
  96 + <groupId>junit</groupId>
  97 + <artifactId>junit</artifactId>
  98 + <scope>test</scope>
  99 + </dependency>
  100 +
94 101 <!-- 工具类相关 -->
95 102  
96 103 </dependencies>
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java
... ... @@ -5,6 +5,9 @@ import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
7 7 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  8 +import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
  9 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
  10 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
8 11 import io.swagger.annotations.Api;
9 12 import io.swagger.annotations.ApiOperation;
10 13 import lombok.extern.slf4j.Slf4j;
... ... @@ -13,10 +16,12 @@ import org.springframework.web.bind.annotation.*;
13 16 import org.springframework.web.multipart.MultipartFile;
14 17  
15 18 import javax.annotation.Resource;
  19 +import javax.validation.Valid;
16 20  
17 21 import java.io.IOException;
18 22  
19 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  24 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
20 25 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
21 26 import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.FILE_IS_EMPTY;
22 27  
... ... @@ -30,6 +35,9 @@ public class SysUserProfileController {
30 35 @Resource
31 36 private MbrUserService userService;
32 37  
  38 + @Resource
  39 + private SysSmsCodeService smsCodeService;
  40 +
33 41 @PutMapping("/update-nickname")
34 42 @ApiOperation("修改用户昵称")
35 43 @PreAuthenticated
... ... @@ -56,5 +64,17 @@ public class SysUserProfileController {
56 64 return success(userService.getUserInfo(getLoginUserId()));
57 65 }
58 66  
  67 +
  68 + @PostMapping("/update-mobile")
  69 + @ApiOperation(value = "修改用户手机")
  70 + @PreAuthenticated
  71 + public CommonResult<Boolean> updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) {
  72 + // 校验验证码
  73 + smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
  74 +
  75 + userService.updateMobile(getLoginUserId(), reqVO);
  76 + return success(true);
  77 + }
  78 +
59 79 }
60 80  
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +import lombok.NoArgsConstructor;
  9 +import org.hibernate.validator.constraints.Length;
  10 +
  11 +import javax.validation.constraints.NotBlank;
  12 +import javax.validation.constraints.NotEmpty;
  13 +import javax.validation.constraints.Pattern;
  14 +
  15 +@ApiModel("修改手机 Request VO")
  16 +@Data
  17 +@NoArgsConstructor
  18 +@AllArgsConstructor
  19 +@Builder
  20 +public class MbrUserUpdateMobileReqVO {
  21 +
  22 + @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
  23 + @NotEmpty(message = "手机验证码不能为空")
  24 + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
  25 + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
  26 + private String code;
  27 +
  28 +
  29 + @ApiModelProperty(value = "手机号",required = true,example = "15823654487")
  30 + @NotBlank(message = "手机号不能为空")
  31 + @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
  32 + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误")
  33 + private String mobile;
  34 +
  35 +}
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.userserver.modules.member.service.user;
3 3 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
4 4 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
5 5 import cn.iocoder.yudao.framework.common.validation.Mobile;
  6 +import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
6 7  
7 8 import java.io.InputStream;
8 9  
... ... @@ -69,4 +70,11 @@ public interface MbrUserService {
69 70 */
70 71 MbrUserInfoRespVO getUserInfo(Long userId);
71 72  
  73 + /**
  74 + * 修改手机
  75 + * @param userId 用户id
  76 + * @param reqVO 请求实体
  77 + */
  78 + void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO);
  79 +
72 80 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
... ... @@ -6,8 +6,10 @@ import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreServic
6 6 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
7 7 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
8 8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  9 +import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
9 10 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
10 11 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  12 +import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
11 13 import com.google.common.annotations.VisibleForTesting;
12 14 import lombok.extern.slf4j.Slf4j;
13 15 import org.springframework.security.crypto.password.PasswordEncoder;
... ... @@ -40,6 +42,9 @@ public class MbrUserServiceImpl implements MbrUserService {
40 42 @Resource
41 43 private PasswordEncoder passwordEncoder;
42 44  
  45 + @Resource
  46 + private SysAuthService sysAuthService;
  47 +
43 48 @Override
44 49 public MbrUserDO getUserByMobile(String mobile) {
45 50 return userMapper.selectByMobile(mobile);
... ... @@ -116,6 +121,17 @@ public class MbrUserServiceImpl implements MbrUserService {
116 121 return userResp;
117 122 }
118 123  
  124 + @Override
  125 + public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) {
  126 + // 检测用户是否存在
  127 + MbrUserDO userDO = checkUserExists(userId);
  128 + // 检测手机与验证码是否匹配
  129 + sysAuthService.checkIfMobileMatchCodeAndDeleteCode(userDO.getMobile(),reqVO.getCode());
  130 + // 更新用户手机
  131 + userDO.setMobile(reqVO.getMobile());
  132 + userMapper.updateById(userDO);
  133 + }
  134 +
119 135 @VisibleForTesting
120 136 public MbrUserDO checkUserExists(Long id) {
121 137 if (id == null) {
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
... ... @@ -3,7 +3,9 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth;
3 3 import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
4 4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
5 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
6 7 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
  8 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
7 9 import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
8 10 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
9 11 import com.alibaba.fastjson.JSON;
... ... @@ -56,16 +58,47 @@ public class SysAuthController {
56 58 }
57 59  
58 60 @PostMapping("/send-sms-code")
59   - @ApiOperation("发送手机验证码")
  61 + @ApiOperation(value = "发送手机验证码",notes = "不检测该手机号是否已被注册")
60 62 public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {
61 63 smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP());
62 64 return success(true);
63 65 }
64 66  
  67 + @PostMapping("/send-sms-new-code")
  68 + @ApiOperation(value = "发送手机验证码",notes = "检测该手机号是否已被注册,用于修改手机时使用")
  69 + public CommonResult<Boolean> sendSmsNewCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {
  70 + smsCodeService.sendSmsNewCode(reqVO);
  71 + return success(true);
  72 + }
  73 +
  74 + @GetMapping("/send-sms-code-login")
  75 + @ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用")
  76 + public CommonResult<Boolean> sendSmsCodeLogin() {
  77 + smsCodeService.sendSmsCodeLogin(getLoginUserId());
  78 + return success(true);
  79 + }
  80 +
65 81 @PostMapping("/reset-password")
66 82 @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
67   - public CommonResult<Boolean> resetPassword(@RequestBody @Valid MbrAuthResetPasswordReqVO reqVO) {
68   - return null;
  83 + public CommonResult<Boolean> resetPassword(@RequestBody @Validated(MbrAuthResetPasswordReqVO.resetPasswordValidView.class) MbrAuthResetPasswordReqVO reqVO) {
  84 + authService.resetPassword(reqVO);
  85 + return success(true);
  86 + }
  87 +
  88 + @PostMapping("/update-password")
  89 + @ApiOperation(value = "修改用户密码",notes = "用户修改密码时使用")
  90 + @PreAuthenticated
  91 + public CommonResult<Boolean> updatePassword(@RequestBody @Validated(MbrAuthResetPasswordReqVO.updatePasswordValidView.class) MbrAuthResetPasswordReqVO reqVO) {
  92 + authService.updatePassword(getLoginUserId(), reqVO);
  93 + return success(true);
  94 + }
  95 +
  96 + @PostMapping("/check-sms-code")
  97 + @ApiOperation(value = "校验验证码是否正确")
  98 + @PreAuthenticated
  99 + public CommonResult<Boolean> checkSmsCode(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) {
  100 + smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHECK_CODE_BY_SMS.getScene(),reqVO.getCode(),getClientIP());
  101 + return success(true);
69 102 }
70 103  
71 104 // ========== 社交登录相关 ==========
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
... ... @@ -8,6 +8,7 @@ import lombok.Data;
8 8 import lombok.NoArgsConstructor;
9 9 import org.hibernate.validator.constraints.Length;
10 10  
  11 +import javax.validation.constraints.NotBlank;
11 12 import javax.validation.constraints.NotEmpty;
12 13 import javax.validation.constraints.Pattern;
13 14  
... ... @@ -18,15 +19,31 @@ import javax.validation.constraints.Pattern;
18 19 @Builder
19 20 public class MbrAuthResetPasswordReqVO {
20 21  
  22 + /**
  23 + * 修改密码校验规则
  24 + */
  25 + public interface updatePasswordValidView {
  26 + }
  27 +
  28 + /**
  29 + * 忘记密码校验规则
  30 + */
  31 + public interface resetPasswordValidView {
  32 + }
  33 +
  34 + @ApiModelProperty(value = "用户旧密码", required = true, example = "123456")
  35 + @NotBlank(message = "旧密码不能为空",groups = updatePasswordValidView.class)
  36 + @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
  37 + private String oldPassword;
  38 +
21 39 @ApiModelProperty(value = "新密码", required = true, example = "buzhidao")
22   - @NotEmpty(message = "新密码不能为空")
  40 + @NotEmpty(message = "新密码不能为空",groups = {updatePasswordValidView.class,resetPasswordValidView.class})
23 41 @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
24 42 private String password;
25 43  
26 44 @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
27   - @NotEmpty(message = "手机验证码不能为空")
  45 + @NotEmpty(message = "手机验证码不能为空",groups = resetPasswordValidView.class)
28 46 @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
29 47 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
30 48 private String code;
31   -
32 49 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java
... ... @@ -23,7 +23,10 @@ public interface SysErrorCodeConstants {
23 23 ErrorCode USER_SMS_CODE_NOT_CORRECT = new ErrorCode(1005001003, "验证码不正确");
24 24 ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量");
25 25 ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率");
  26 + ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用");
26 27  
27 28 // ========== 用户模块 1005002000 ==========
28 29 ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在");
  30 + ErrorCode USER_CODE_FAILED = new ErrorCode(1005002002, "验证码不匹配");
  31 + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败");
29 32 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsSceneEnum.java
... ... @@ -17,7 +17,9 @@ public enum SysSmsSceneEnum implements IntArrayValuable {
17 17  
18 18 LOGIN_BY_SMS(1, "手机号登陆"),
19 19 CHANGE_MOBILE_BY_SMS(2, "更换手机号"),
20   - ;
  20 + FORGET_MOBILE_BY_SMS(3, "忘记密码"),
  21 + CHECK_CODE_BY_SMS(4, "审核验证码"),
  22 + ;
21 23  
22 24 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray();
23 25  
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java
... ... @@ -63,4 +63,23 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
63 63 */
64 64 void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO);
65 65  
  66 + /**
  67 + * 修改用户密码
  68 + * @param userId 用户id
  69 + * @param userReqVO 用户请求实体类
  70 + */
  71 + void updatePassword(Long userId, MbrAuthResetPasswordReqVO userReqVO);
  72 +
  73 + /**
  74 + * 忘记密码
  75 + * @param userReqVO 用户请求实体类
  76 + */
  77 + void resetPassword(MbrAuthResetPasswordReqVO userReqVO);
  78 +
  79 + /**
  80 + * 检测手机与验证码是否匹配
  81 + * @param phone 手机号
  82 + * @param code 验证码
  83 + */
  84 + void checkIfMobileMatchCodeAndDeleteCode(String phone,String code);
66 85 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
... ... @@ -15,15 +15,19 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
15 15 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
16 16 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
17 17 import cn.iocoder.yudao.framework.security.core.LoginUser;
  18 +import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
18 19 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
19 20 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
20 21 import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert;
21 22 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
22 23 import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
23 24 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
  25 +import com.google.common.annotations.VisibleForTesting;
24 26 import lombok.extern.slf4j.Slf4j;
25 27 import me.zhyd.oauth.model.AuthUser;
  28 +import org.springframework.beans.factory.annotation.Autowired;
26 29 import org.springframework.context.annotation.Lazy;
  30 +import org.springframework.data.redis.core.StringRedisTemplate;
27 31 import org.springframework.security.authentication.AuthenticationManager;
28 32 import org.springframework.security.authentication.BadCredentialsException;
29 33 import org.springframework.security.authentication.DisabledException;
... ... @@ -32,6 +36,7 @@ import org.springframework.security.core.Authentication;
32 36 import org.springframework.security.core.AuthenticationException;
33 37 import org.springframework.security.core.userdetails.UserDetails;
34 38 import org.springframework.security.core.userdetails.UsernameNotFoundException;
  39 +import org.springframework.security.crypto.password.PasswordEncoder;
35 40 import org.springframework.stereotype.Service;
36 41 import org.springframework.transaction.annotation.Transactional;
37 42  
... ... @@ -40,6 +45,7 @@ import java.util.List;
40 45 import java.util.Objects;
41 46  
42 47 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  48 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
43 49 import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*;
44 50  
45 51 /**
... ... @@ -65,6 +71,13 @@ public class SysAuthServiceImpl implements SysAuthService {
65 71 private SysUserSessionCoreService userSessionCoreService;
66 72 @Resource
67 73 private SysSocialService socialService;
  74 + @Resource
  75 + private StringRedisTemplate stringRedisTemplate;
  76 + @Resource
  77 + private PasswordEncoder passwordEncoder;
  78 + @Resource
  79 + private MbrUserMapper userMapper;
  80 +
68 81 private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER;
69 82  
70 83 @Override
... ... @@ -200,12 +213,12 @@ public class SysAuthServiceImpl implements SysAuthService {
200 213 }
201 214 reqDTO.setUsername(mobile);
202 215 reqDTO.setUserAgent(ServletUtils.getUserAgent());
203   - reqDTO.setUserIp(ServletUtils.getClientIP());
  216 + reqDTO.setUserIp(getClientIP());
204 217 reqDTO.setResult(loginResult.getResult());
205 218 loginLogCoreService.createLoginLog(reqDTO);
206 219 // 更新最后登录时间
207 220 if (user != null && Objects.equals(SysLoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
208   - userService.updateUserLogin(user.getId(), ServletUtils.getClientIP());
  221 + userService.updateUserLogin(user.getId(), getClientIP());
209 222 }
210 223 }
211 224  
... ... @@ -266,6 +279,66 @@ public class SysAuthServiceImpl implements SysAuthService {
266 279 this.createLogoutLog(loginUser.getId(), loginUser.getUsername());
267 280 }
268 281  
  282 + @Override
  283 + public void updatePassword(Long userId, MbrAuthResetPasswordReqVO reqVO) {
  284 + // 检验旧密码
  285 + MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword());
  286 +
  287 + // 更新用户密码
  288 + userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
  289 + userMapper.updateById(userDO);
  290 + }
  291 +
  292 + @Override
  293 + public void resetPassword(MbrAuthResetPasswordReqVO reqVO) {
  294 + // 根据验证码取出手机号,并查询用户
  295 + String mobile = stringRedisTemplate.opsForValue().get(reqVO.getCode());
  296 + MbrUserDO userDO = userMapper.selectByMobile(mobile);
  297 + if (userDO == null){
  298 + throw exception(USER_NOT_EXISTS);
  299 + }
  300 + // TODO @芋艿 这一步没必要检验验证码与手机是否匹配,因为是根据验证码去redis中查找手机号,然后根据手机号查询用户
  301 + // 也就是说 即便黑客以其他方式将验证码发送到自己手机上,最终还是会根据手机号查询用户然后进行重置密码的操作,不存在安全问题
  302 +
  303 + // 校验验证码
  304 + smsCodeService.useSmsCode(userDO.getMobile(), SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
  305 +
  306 + // 更新密码
  307 + userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
  308 + userMapper.updateById(userDO);
  309 + }
  310 +
  311 + @Override
  312 + public void checkIfMobileMatchCodeAndDeleteCode(String phone, String code) {
  313 + // 检验用户手机与验证码是否匹配
  314 + String mobile = stringRedisTemplate.opsForValue().get(code);
  315 + if (!phone.equals(mobile)){
  316 + throw exception(USER_CODE_FAILED);
  317 + }
  318 + // 销毁redis中此验证码
  319 + stringRedisTemplate.delete(code);
  320 + }
  321 +
  322 + /**
  323 + * 校验旧密码
  324 + *
  325 + * @param id 用户 id
  326 + * @param oldPassword 旧密码
  327 + * @return MbrUserDO 用户实体
  328 + */
  329 + @VisibleForTesting
  330 + public MbrUserDO checkOldPassword(Long id, String oldPassword) {
  331 + MbrUserDO user = userMapper.selectById(id);
  332 + if (user == null) {
  333 + throw exception(USER_NOT_EXISTS);
  334 + }
  335 + // 参数:未加密密码,编码后的密码
  336 + if (!passwordEncoder.matches(oldPassword,user.getPassword())) {
  337 + throw exception(USER_PASSWORD_FAILED);
  338 + }
  339 + return user;
  340 + }
  341 +
269 342 private void createLogoutLog(Long userId, String username) {
270 343 SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
271 344 reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
... ... @@ -274,7 +347,7 @@ public class SysAuthServiceImpl implements SysAuthService {
274 347 reqDTO.setUserType(userTypeEnum.getValue());
275 348 reqDTO.setUsername(username);
276 349 reqDTO.setUserAgent(ServletUtils.getUserAgent());
277   - reqDTO.setUserIp(ServletUtils.getClientIP());
  350 + reqDTO.setUserIp(getClientIP());
278 351 reqDTO.setResult(SysLoginResultEnum.SUCCESS.getResult());
279 352 loginLogCoreService.createLoginLog(reqDTO);
280 353 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.userserver.modules.system.service.sms;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 4 import cn.iocoder.yudao.framework.common.validation.Mobile;
  5 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;
5 6 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
6 7  
7 8 /**
... ... @@ -21,6 +22,12 @@ public interface SysSmsCodeService {
21 22 void sendSmsCode(@Mobile String mobile, Integer scene, String createIp);
22 23  
23 24 /**
  25 + * 发送短信验证码,并检测手机号是否已被注册
  26 + * @param reqVO 请求实体
  27 + */
  28 + void sendSmsNewCode(SysAuthSendSmsReqVO reqVO);
  29 +
  30 + /**
24 31 * 验证短信验证码,并进行使用
25 32 * 如果正确,则将验证码标记成已使用
26 33 * 如果错误,则抛出 {@link ServiceException} 异常
... ... @@ -32,4 +39,9 @@ public interface SysSmsCodeService {
32 39 */
33 40 void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp);
34 41  
  42 + /**
  43 + * 根据用户id发送验证码
  44 + * @param userId 用户id
  45 + */
  46 + void sendSmsCodeLogin(Long userId);
35 47 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java
1 1 package cn.iocoder.yudao.userserver.modules.system.service.sms.impl;
2 2  
3 3 import cn.hutool.core.map.MapUtil;
  4 +import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
4 5 import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
  6 +import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  7 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;
5 8 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
6 9 import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper;
  10 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
7 11 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsTemplateCodeConstants;
8 12 import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties;
9 13 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
  14 +import org.springframework.data.redis.core.StringRedisTemplate;
10 15 import org.springframework.stereotype.Service;
11 16 import org.springframework.validation.annotation.Validated;
12 17  
13 18 import javax.annotation.Resource;
14 19 import java.util.Date;
  20 +import java.util.concurrent.TimeUnit;
15 21  
16 22 import static cn.hutool.core.util.RandomUtil.randomInt;
17 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  24 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
18 25 import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*;
19 26  
20 27 /**
... ... @@ -26,6 +33,11 @@ import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConst
26 33 @Validated
27 34 public class SysSmsCodeServiceImpl implements SysSmsCodeService {
28 35  
  36 + /**
  37 + * 验证码 + 手机 在redis中存储的有效时间,单位:分钟
  38 + */
  39 + private static final Long CODE_TIME = 10L;
  40 +
29 41 @Resource
30 42 private SmsCodeProperties smsCodeProperties;
31 43  
... ... @@ -33,8 +45,14 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
33 45 private SysSmsCodeMapper smsCodeMapper;
34 46  
35 47 @Resource
  48 + private MbrUserService mbrUserService;
  49 +
  50 + @Resource
36 51 private SysSmsCoreService smsCoreService;
37 52  
  53 + @Resource
  54 + private StringRedisTemplate stringRedisTemplate;
  55 +
38 56 @Override
39 57 public void sendSmsCode(String mobile, Integer scene, String createIp) {
40 58 // 创建验证码
... ... @@ -42,6 +60,21 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
42 60 // 发送验证码
43 61 smsCoreService.sendSingleSmsToMember(mobile, null, SysSmsTemplateCodeConstants.USER_SMS_LOGIN,
44 62 MapUtil.of("code", code));
  63 +
  64 + // 存储手机号与验证码到redis,用于标记
  65 + stringRedisTemplate.opsForValue().set(code,mobile,CODE_TIME, TimeUnit.MINUTES);
  66 + }
  67 +
  68 + @Override
  69 + public void sendSmsNewCode(SysAuthSendSmsReqVO reqVO) {
  70 + // 检测手机号是否已被使用
  71 + MbrUserDO userByMobile = mbrUserService.getUserByMobile(reqVO.getMobile());
  72 + if (userByMobile != null){
  73 + throw exception(USER_SMS_CODE_IS_EXISTS);
  74 + }
  75 +
  76 + // 发送短信
  77 + this.sendSmsCode(reqVO.getMobile(),reqVO.getScene(),getClientIP());
45 78 }
46 79  
47 80 private String createSmsCode(String mobile, Integer scene, String ip) {
... ... @@ -91,4 +124,14 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
91 124 .used(true).usedTime(new Date()).usedIp(usedIp).build());
92 125 }
93 126  
  127 + @Override
  128 + public void sendSmsCodeLogin(Long userId) {
  129 + MbrUserDO user = mbrUserService.getUser(userId);
  130 + if (user == null){
  131 + throw exception(USER_NOT_EXISTS);
  132 + }
  133 + // 发送验证码
  134 + this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP());
  135 + }
  136 +
94 137 }
... ...
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbAndRedisUnitTest.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver;
  2 +
  3 +import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
  4 +import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
  5 +import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
  6 +import cn.iocoder.yudao.userserver.config.RedisTestConfiguration;
  7 +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
  8 +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
  9 +import org.redisson.spring.starter.RedissonAutoConfiguration;
  10 +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
  11 +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
  12 +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
  13 +import org.springframework.boot.test.context.SpringBootTest;
  14 +import org.springframework.context.annotation.Import;
  15 +import org.springframework.test.context.ActiveProfiles;
  16 +import org.springframework.test.context.jdbc.Sql;
  17 +
  18 +/**
  19 + * 依赖内存 DB + Redis 的单元测试
  20 + *
  21 + * 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
  26 +@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
  27 +@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
  28 +public class BaseDbAndRedisUnitTest {
  29 +
  30 + @Import({
  31 + // DB 配置类
  32 + YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
  33 + DataSourceAutoConfiguration.class, // Spring DB 自动配置类
  34 + DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
  35 + DruidDataSourceAutoConfigure.class, // Druid 自动配置类
  36 + // MyBatis 配置类
  37 + YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
  38 + MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
  39 + // Redis 配置类
  40 + RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
  41 + RedisAutoConfiguration.class, // Spring Redis 自动配置类
  42 + YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
  43 + RedissonAutoConfiguration.class, // Redisson 自动高配置类
  44 + })
  45 + public static class Application {
  46 + }
  47 +
  48 +}
... ...
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/config/RedisTestConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.config;
  2 +
  3 +import com.github.fppt.jedismock.RedisServer;
  4 +import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
  5 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
  6 +import org.springframework.context.annotation.Bean;
  7 +import org.springframework.context.annotation.Configuration;
  8 +import org.springframework.context.annotation.Lazy;
  9 +
  10 +import java.io.IOException;
  11 +
  12 +@Configuration(proxyBeanMethods = false)
  13 +@Lazy(false) // 禁止延迟加载
  14 +@EnableConfigurationProperties(RedisProperties.class)
  15 +public class RedisTestConfiguration {
  16 +
  17 + /**
  18 + * 创建模拟的 Redis Server 服务器
  19 + */
  20 + @Bean
  21 + public RedisServer redisServer(RedisProperties properties) throws IOException {
  22 + RedisServer redisServer = new RedisServer(properties.getPort());
  23 + // TODO 芋艿:一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样,就导致端口被占用,无法启动。。。
  24 + try {
  25 + redisServer.start();
  26 + } catch (Exception ignore) {}
  27 + return redisServer;
  28 + }
  29 +
  30 +}
... ...
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.member.controller;
  2 +
  3 +import cn.iocoder.yudao.userserver.modules.member.controller.user.SysUserProfileController;
  4 +import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  5 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
  6 +import org.junit.Before;
  7 +import org.junit.Test;
  8 +import org.mockito.InjectMocks;
  9 +import org.mockito.Mock;
  10 +import org.mockito.MockitoAnnotations;
  11 +import org.springframework.http.MediaType;
  12 +import org.springframework.test.web.servlet.MockMvc;
  13 +import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
  14 +import org.springframework.test.web.servlet.setup.MockMvcBuilders;
  15 +
  16 +
  17 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
  18 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  19 +
  20 +/**
  21 + * {@link SysUserProfileController} 的单元测试类
  22 + *
  23 + * @author 宋天
  24 + */
  25 +public class SysUserProfileControllerTest {
  26 +
  27 + private MockMvc mockMvc;
  28 +
  29 + @InjectMocks
  30 + private SysUserProfileController sysUserProfileController;
  31 +
  32 + @Mock
  33 + private MbrUserService userService;
  34 +
  35 + @Mock
  36 + private SysSmsCodeService smsCodeService;
  37 +
  38 + @Before
  39 + public void setup() {
  40 + // 初始化
  41 + MockitoAnnotations.openMocks(this);
  42 +
  43 + // 构建mvc环境
  44 + mockMvc = MockMvcBuilders.standaloneSetup(sysUserProfileController).build();
  45 + }
  46 +
  47 + @Test
  48 + public void testUpdateMobile_success() throws Exception {
  49 + //模拟接口调用
  50 + this.mockMvc.perform(post("/system/user/profile/update-mobile")
  51 + .contentType(MediaType.APPLICATION_JSON_VALUE)
  52 + .content("{\"mobile\":\"15819844280\",\"code\":\"123456\"}}"))
  53 + .andExpect(status().isOk())
  54 + .andDo(MockMvcResultHandlers.print());
  55 +
  56 + }
  57 +
  58 +
  59 +}
... ...
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/service/MbrUserServiceImplTest.java
... ... @@ -2,28 +2,32 @@ package cn.iocoder.yudao.userserver.modules.member.service;
2 2  
3 3 import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
4 4 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
5   -import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
6 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 6 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
  7 +import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
  8 +import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest;
8 9 import cn.iocoder.yudao.userserver.BaseDbUnitTest;
9 10 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
  11 +import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
10 12 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
11 13 import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl;
  14 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;
  15 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
  16 +import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl;
  17 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
  18 +import cn.iocoder.yudao.userserver.modules.system.service.sms.impl.SysSmsCodeServiceImpl;
12 19 import org.junit.jupiter.api.Test;
13 20 import org.springframework.boot.test.mock.mockito.MockBean;
14 21 import org.springframework.context.annotation.Import;
15   -import org.springframework.http.MediaType;
16   -import org.springframework.mock.web.MockMultipartFile;
  22 +import org.springframework.data.redis.core.StringRedisTemplate;
17 23 import org.springframework.security.crypto.password.PasswordEncoder;
18   -import org.springframework.util.Assert;
19 24  
20 25 import javax.annotation.Resource;
21 26 import java.io.*;
22 27 import java.util.function.Consumer;
23 28  
24   -import static cn.hutool.core.util.RandomUtil.randomBytes;
  29 +import static cn.hutool.core.util.RandomUtil.*;
25 30 import static org.junit.jupiter.api.Assertions.assertEquals;
26   -import static cn.hutool.core.util.RandomUtil.randomEle;
27 31 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
28 32 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
29 33 import static org.mockito.Mockito.*;
... ... @@ -32,21 +36,30 @@ import static org.mockito.Mockito.*;
32 36 *
33 37 * @author 宋天
34 38 */
35   -@Import(MbrUserServiceImpl.class)
36   -public class MbrUserServiceImplTest extends BaseDbUnitTest {
  39 +@Import({MbrUserServiceImpl.class, YudaoRedisAutoConfiguration.class})
  40 +public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
37 41  
38 42 @Resource
39 43 private MbrUserServiceImpl mbrUserService;
40 44  
41 45 @Resource
  46 + private StringRedisTemplate stringRedisTemplate;
  47 +
  48 + @Resource
42 49 private MbrUserMapper userMapper;
43 50  
44 51 @MockBean
  52 + private SysAuthServiceImpl authService;
  53 +
  54 + @MockBean
45 55 private InfFileCoreService fileCoreService;
46 56  
47 57 @MockBean
48 58 private PasswordEncoder passwordEncoder;
49 59  
  60 + @MockBean
  61 + private SysSmsCodeService sysSmsCodeService;
  62 +
50 63 @Test
51 64 public void testUpdateNickName_success(){
52 65 // mock 数据
... ... @@ -94,6 +107,37 @@ public class MbrUserServiceImplTest extends BaseDbUnitTest {
94 107 assertEquals(avatar, str);
95 108 }
96 109  
  110 + @Test
  111 + public void updateMobile_success(){
  112 + // mock数据
  113 + String oldMobile = randomNumbers(11);
  114 + MbrUserDO userDO = randomMbrUserDO();
  115 + userDO.setMobile(oldMobile);
  116 + userMapper.insert(userDO);
  117 +
  118 + // 验证旧手机
  119 + sysSmsCodeService.sendSmsCodeLogin(userDO.getId());
  120 +
  121 + // 验证旧手机验证码是否正确
  122 + sysSmsCodeService.useSmsCode(oldMobile,SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(),"123","1.1.1.1");
  123 +
  124 + // 验证新手机
  125 + SysAuthSendSmsReqVO smsReqVO = new SysAuthSendSmsReqVO();
  126 + smsReqVO.setMobile(oldMobile);
  127 + smsReqVO.setScene(SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
  128 + sysSmsCodeService.sendSmsNewCode(smsReqVO);
  129 +
  130 + // 更新手机号
  131 + String newMobile = randomNumbers(11);
  132 + String code = randomNumbers(4);
  133 + MbrUserUpdateMobileReqVO reqVO = new MbrUserUpdateMobileReqVO();
  134 + reqVO.setMobile(newMobile);
  135 + reqVO.setCode(code);
  136 + mbrUserService.updateMobile(userDO.getId(),reqVO);
  137 +
  138 + assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile);
  139 + }
  140 +
97 141 // ========== 随机对象 ==========
98 142  
99 143 @SafeVarargs
... ...
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.controller;
  2 +
  3 +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
  4 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.SysAuthController;
  5 +import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
  6 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
  7 +import org.junit.Before;
  8 +import org.junit.Test;
  9 +import org.mockito.InjectMocks;
  10 +import org.mockito.Mock;
  11 +import org.mockito.MockitoAnnotations;
  12 +import org.springframework.http.MediaType;
  13 +import org.springframework.test.web.servlet.MockMvc;
  14 +import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
  15 +import org.springframework.test.web.servlet.setup.MockMvcBuilders;
  16 +
  17 +import static org.springframework.http.HttpHeaders.AUTHORIZATION;
  18 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
  19 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
  20 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  21 +
  22 +/**
  23 + * {@link SysAuthController} 的单元测试类
  24 + *
  25 + * @author 宋天
  26 + */
  27 +public class SysAuthControllerTest {
  28 +
  29 + private MockMvc mockMvc;
  30 +
  31 + @InjectMocks
  32 + private SysAuthController sysAuthController;
  33 +
  34 + @Mock
  35 + private SysAuthService authService;
  36 + @Mock
  37 + private SysSmsCodeService smsCodeService;
  38 + @Mock
  39 + private SysSocialService socialService;
  40 +
  41 +
  42 + @Before
  43 + public void setup() {
  44 + // 初始化
  45 + MockitoAnnotations.openMocks(this);
  46 +
  47 + // 构建mvc环境
  48 + mockMvc = MockMvcBuilders.standaloneSetup(sysAuthController).build();
  49 + }
  50 +
  51 + @Test
  52 + public void testResetPassword_success() throws Exception {
  53 + //模拟接口调用
  54 + this.mockMvc.perform(post("/reset-password")
  55 + .contentType(MediaType.APPLICATION_JSON)
  56 + .content("{\"password\":\"1123\",\"code\":\"123456\"}}"))
  57 + .andExpect(status().isOk())
  58 + .andDo(MockMvcResultHandlers.print());
  59 +
  60 + }
  61 +
  62 + @Test
  63 + public void testUpdatePassword_success() throws Exception {
  64 + //模拟接口调用
  65 + this.mockMvc.perform(post("/update-password")
  66 + .contentType(MediaType.APPLICATION_JSON)
  67 + .content("{\"password\":\"1123\",\"code\":\"123456\",\"oldPassword\":\"1123\"}}"))
  68 + .andExpect(status().isOk())
  69 + .andDo(MockMvcResultHandlers.print());
  70 +
  71 + }
  72 +
  73 +
  74 +
  75 +}
... ...
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/service/SysAuthServiceTest.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.service;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
  5 +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
  6 +import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
  7 +import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
  8 +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
  9 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  10 +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
  11 +import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
  12 +import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest;
  13 +import cn.iocoder.yudao.userserver.BaseDbUnitTest;
  14 +import cn.iocoder.yudao.userserver.config.RedisTestConfiguration;
  15 +import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
  16 +import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  17 +import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl;
  18 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.MbrAuthResetPasswordReqVO;
  19 +import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
  20 +import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl;
  21 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
  22 +import org.junit.jupiter.api.Test;
  23 +import org.springframework.boot.test.mock.mockito.MockBean;
  24 +import org.springframework.context.annotation.Import;
  25 +import org.springframework.data.redis.core.StringRedisTemplate;
  26 +import org.springframework.security.authentication.AuthenticationManager;
  27 +import org.springframework.security.crypto.password.PasswordEncoder;
  28 +
  29 +import javax.annotation.Resource;
  30 +
  31 +import java.util.concurrent.TimeUnit;
  32 +import java.util.function.Consumer;
  33 +
  34 +import static cn.hutool.core.util.RandomUtil.randomEle;
  35 +import static cn.hutool.core.util.RandomUtil.randomNumbers;
  36 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  37 +import static org.junit.jupiter.api.Assertions.assertEquals;
  38 +import static org.mockito.Mockito.*;
  39 +
  40 +
  41 +/**
  42 + * {@link SysAuthService} 的单元测试类
  43 + *
  44 + * @author 宋天
  45 + */
  46 +@Import({SysAuthServiceImpl.class, YudaoRedisAutoConfiguration.class})
  47 +public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
  48 +
  49 + @MockBean
  50 + private AuthenticationManager authenticationManager;
  51 + @MockBean
  52 + private MbrUserService userService;
  53 + @MockBean
  54 + private SysSmsCodeService smsCodeService;
  55 + @MockBean
  56 + private SysLoginLogCoreService loginLogCoreService;
  57 + @MockBean
  58 + private SysUserSessionCoreService userSessionCoreService;
  59 + @MockBean
  60 + private SysSocialService socialService;
  61 + @Resource
  62 + private StringRedisTemplate stringRedisTemplate;
  63 + @MockBean
  64 + private PasswordEncoder passwordEncoder;
  65 + @Resource
  66 + private MbrUserMapper mbrUserMapper;
  67 + @Resource
  68 + private SysAuthServiceImpl authService;
  69 +
  70 +
  71 + @Test
  72 + public void testUpdatePassword_success(){
  73 + // 准备参数
  74 + MbrUserDO userDO = randomMbrUserDO();
  75 + mbrUserMapper.insert(userDO);
  76 +
  77 + // 新密码
  78 + String newPassword = randomString();
  79 +
  80 + // 请求实体
  81 + MbrAuthResetPasswordReqVO reqVO = new MbrAuthResetPasswordReqVO();
  82 + reqVO.setOldPassword(userDO.getPassword());
  83 + reqVO.setPassword(newPassword);
  84 +
  85 + // 测试桩
  86 + // 这两个相等是为了返回ture这个结果
  87 + when(passwordEncoder.matches(reqVO.getOldPassword(),reqVO.getOldPassword())).thenReturn(true);
  88 + when(passwordEncoder.encode(newPassword)).thenReturn(newPassword);
  89 +
  90 + // 更新用户密码
  91 + authService.updatePassword(userDO.getId(),reqVO);
  92 + assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),newPassword);
  93 + }
  94 +
  95 + @Test
  96 + public void testResetPassword_success(){
  97 + // 准备参数
  98 + MbrUserDO userDO = randomMbrUserDO();
  99 + mbrUserMapper.insert(userDO);
  100 +
  101 + // 随机密码
  102 + String password = randomNumbers(11);
  103 + // 随机验证码
  104 + String code = randomNumbers(4);
  105 +
  106 + MbrAuthResetPasswordReqVO reqVO = new MbrAuthResetPasswordReqVO();
  107 + reqVO.setPassword(password);
  108 + reqVO.setCode(code);
  109 +
  110 + // 放入code+手机号
  111 + stringRedisTemplate.opsForValue().set(code,userDO.getMobile(),10, TimeUnit.MINUTES);
  112 +
  113 + // mock
  114 + when(passwordEncoder.encode(password)).thenReturn(password);
  115 +
  116 + // 更新用户密码
  117 + authService.resetPassword(reqVO);
  118 + assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),password);
  119 + }
  120 +
  121 +
  122 + // ========== 随机对象 ==========
  123 +
  124 + @SafeVarargs
  125 + private static MbrUserDO randomMbrUserDO(Consumer<MbrUserDO>... consumers) {
  126 + Consumer<MbrUserDO> consumer = (o) -> {
  127 + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
  128 + o.setPassword(randomString());
  129 + };
  130 + return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers));
  131 + }
  132 +
  133 +
  134 +}
... ...