Commit 8e0569ee544b26cc6944c7eab6bb92d82bca57f6
1 parent
c84e19e1
update 新增接口:修改手机,修改密码,忘记密码
Showing
19 changed files
with
696 additions
and
18 deletions
yudao-user-server/pom.xml
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 | +} | ... | ... |