mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-30 01:38:43 +08:00 
			
		
		
		
	Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/user-social
Conflicts: yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java yudao-dependencies/pom.xml yudao-framework/pom.xml yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
This commit is contained in:
		| @@ -1,9 +0,0 @@ | ||||
| package cn.iocoder.yudao.userserver.framework.async.config; | ||||
|  | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.scheduling.annotation.EnableAsync; | ||||
|  | ||||
| @Configuration | ||||
| @EnableAsync | ||||
| public class AsyncConfiguration { | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| /** | ||||
|  * 异步执行,基于 Spring @Async 实现 | ||||
|  */ | ||||
| package cn.iocoder.yudao.userserver.framework.async; | ||||
| @@ -1 +0,0 @@ | ||||
| <http://www.iocoder.cn/Spring-Boot/Async-Job/?yudao> | ||||
| @@ -1,11 +1,11 @@ | ||||
| ### 请求 /system/user/profile/get 接口 => 没有权限 | ||||
| GET {{userServerUrl}}/system/user/profile/get | ||||
| ### 请求 /member/user/profile/get 接口 => 没有权限 | ||||
| GET {{userServerUrl}}/member/user/profile/get | ||||
| Authorization: Bearer test245 | ||||
|  | ||||
| ### 请求 /system/user/profile/revise-nickname 接口 成功 | ||||
| PUT {{userServerUrl}}/system/user/profile/update-nickname?nickName=yunai222 | ||||
| ### 请求 /member/user/profile/revise-nickname 接口 成功 | ||||
| PUT {{userServerUrl}}/member/user/profile/update-nickname?nickName=yunai222 | ||||
| Authorization: Bearer test245 | ||||
|  | ||||
| ### 请求 /system/user/profile/get-user-info 接口 成功 | ||||
| GET {{userServerUrl}}/system/user/profile/get-user-info?id=245 | ||||
| Authorization: Bearer test245 | ||||
| ### 请求 /member/user/profile/get-user-info 接口 成功 | ||||
| GET {{userServerUrl}}/member/user/profile/get-user-info?id=245 | ||||
| Authorization: Bearer test245 | ||||
|   | ||||
| @@ -5,6 +5,9 @@ import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | ||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||
| import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | ||||
| import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; | ||||
| import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; | ||||
| import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; | ||||
| import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiOperation; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| @@ -13,16 +16,18 @@ import org.springframework.web.bind.annotation.*; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import javax.validation.Valid; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||||
| import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | ||||
| import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | ||||
| import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.FILE_IS_EMPTY; | ||||
|  | ||||
| @Api(tags = "用户个人中心") | ||||
| @RestController | ||||
| @RequestMapping("/system/user/profile") | ||||
| @RequestMapping("/member/user/profile") | ||||
| @Validated | ||||
| @Slf4j | ||||
| public class SysUserProfileController { | ||||
| @@ -30,11 +35,14 @@ public class SysUserProfileController { | ||||
|     @Resource | ||||
|     private MbrUserService userService; | ||||
|  | ||||
|     @Resource | ||||
|     private SysSmsCodeService smsCodeService; | ||||
|  | ||||
|     @PutMapping("/update-nickname") | ||||
|     @ApiOperation("修改用户昵称") | ||||
|     @PreAuthenticated | ||||
|     public CommonResult<Boolean> updateNickname(@RequestParam("nickName") String nickName) { | ||||
|         userService.updateNickname(getLoginUserId(), nickName); | ||||
|     public CommonResult<Boolean> updateNickname(@RequestParam("nickname") String nickname) { | ||||
|         userService.updateNickname(getLoginUserId(), nickname); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
| @@ -49,12 +57,24 @@ public class SysUserProfileController { | ||||
|         return success(avatar); | ||||
|     } | ||||
|  | ||||
|     @GetMapping("/get-user-info") | ||||
|     @ApiOperation("获取用户头像与昵称") | ||||
|     @GetMapping("/get") | ||||
|     @ApiOperation("获得基本信息") | ||||
|     @PreAuthenticated | ||||
|     public CommonResult<MbrUserInfoRespVO> getUserInfo() { | ||||
|         return success(userService.getUserInfo(getLoginUserId())); | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/update-mobile") | ||||
|     @ApiOperation(value = "修改用户手机") | ||||
|     @PreAuthenticated | ||||
|     public CommonResult<Boolean> updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) { | ||||
|         // 校验验证码 | ||||
|         // TODO @宋天:统一到 userService.updateMobile 方法里 | ||||
|         smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP()); | ||||
|  | ||||
|         userService.updateMobile(getLoginUserId(), reqVO); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import lombok.NoArgsConstructor; | ||||
| public class MbrUserInfoRespVO { | ||||
|  | ||||
|     @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") | ||||
|     private String nickName; | ||||
|     private String nickname; | ||||
|  | ||||
|     @ApiModelProperty(value = "用户头像", required = true, example = "/infra/file/get/35a12e57-4297-4faa-bf7d-7ed2f211c952") | ||||
|     private String avatar; | ||||
|   | ||||
| @@ -0,0 +1,35 @@ | ||||
| package cn.iocoder.yudao.userserver.modules.member.controller.user.vo; | ||||
|  | ||||
| import io.swagger.annotations.ApiModel; | ||||
| import io.swagger.annotations.ApiModelProperty; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.hibernate.validator.constraints.Length; | ||||
|  | ||||
| import javax.validation.constraints.NotBlank; | ||||
| import javax.validation.constraints.NotEmpty; | ||||
| import javax.validation.constraints.Pattern; | ||||
|  | ||||
| @ApiModel("修改手机 Request VO") | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| @Builder | ||||
| public class MbrUserUpdateMobileReqVO { | ||||
|  | ||||
|     @ApiModelProperty(value = "手机验证码", required = true, example = "1024") | ||||
|     @NotEmpty(message = "手机验证码不能为空") | ||||
|     @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") | ||||
|     @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") | ||||
|     private String code; | ||||
|  | ||||
|     @ApiModelProperty(value = "手机号",required = true,example = "15823654487") | ||||
|     @NotBlank(message = "手机号不能为空") | ||||
|     // TODO @宋天:手机校验,直接使用 @Mobile 哈 | ||||
|     @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") | ||||
|     @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误") | ||||
|     private String mobile; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,15 @@ | ||||
| package cn.iocoder.yudao.userserver.modules.member.convert.user; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; | ||||
| import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; | ||||
| import org.mapstruct.Mapper; | ||||
| import org.mapstruct.factory.Mappers; | ||||
|  | ||||
| @Mapper | ||||
| public interface UserProfileConvert { | ||||
|  | ||||
|     UserProfileConvert INSTANCE = Mappers.getMapper(UserProfileConvert.class); | ||||
|  | ||||
|     MbrUserInfoRespVO convert(MbrUserDO bean); | ||||
|  | ||||
| } | ||||
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.userserver.modules.member.service.user; | ||||
| import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; | ||||
| import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; | ||||
| import cn.iocoder.yudao.framework.common.validation.Mobile; | ||||
| import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; | ||||
|  | ||||
| import java.io.InputStream; | ||||
|  | ||||
| @@ -50,9 +51,9 @@ public interface MbrUserService { | ||||
|     /** | ||||
|      * 修改用户昵称 | ||||
|      * @param userId 用户id | ||||
|      * @param nickName 用户新昵称 | ||||
|      * @param nickname 用户新昵称 | ||||
|      */ | ||||
|     void updateNickname(Long userId, String nickName); | ||||
|     void updateNickname(Long userId, String nickname); | ||||
|  | ||||
|     /** | ||||
|      * 修改用户头像 | ||||
| @@ -64,9 +65,17 @@ public interface MbrUserService { | ||||
|  | ||||
|     /** | ||||
|      * 根据用户id,获取用户头像与昵称 | ||||
|      * | ||||
|      * @param userId 用户id | ||||
|      * @return 用户响应实体类 | ||||
|      */ | ||||
|     MbrUserInfoRespVO getUserInfo(Long userId); | ||||
|  | ||||
|     /** | ||||
|      * 修改手机 | ||||
|      * @param userId 用户id | ||||
|      * @param reqVO 请求实体 | ||||
|      */ | ||||
|     void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -6,8 +6,11 @@ import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreServic | ||||
| import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; | ||||
| import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; | ||||
| import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert; | ||||
| import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; | ||||
| import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; | ||||
| import com.google.common.annotations.VisibleForTesting; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||||
| @@ -40,6 +43,9 @@ public class MbrUserServiceImpl implements MbrUserService { | ||||
|     @Resource | ||||
|     private PasswordEncoder passwordEncoder; | ||||
|  | ||||
|     @Resource | ||||
|     private SysAuthService sysAuthService; | ||||
|  | ||||
|     @Override | ||||
|     public MbrUserDO getUserByMobile(String mobile) { | ||||
|         return userMapper.selectByMobile(mobile); | ||||
| @@ -80,15 +86,15 @@ public class MbrUserServiceImpl implements MbrUserService { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void updateNickname(Long userId, String nickName) { | ||||
|     public void updateNickname(Long userId, String nickname) { | ||||
|         MbrUserDO user = this.checkUserExists(userId); | ||||
|         // 仅当新昵称不等于旧昵称时进行修改 | ||||
|         if (nickName.equals(user.getNickname())){ | ||||
|         if (nickname.equals(user.getNickname())){ | ||||
|             return; | ||||
|         } | ||||
|         MbrUserDO userDO = new MbrUserDO(); | ||||
|         userDO.setId(user.getId()); | ||||
|         userDO.setNickname(nickName); | ||||
|         userDO.setNickname(nickname); | ||||
|         userMapper.updateById(userDO); | ||||
|     } | ||||
|  | ||||
| @@ -110,10 +116,20 @@ public class MbrUserServiceImpl implements MbrUserService { | ||||
|     public MbrUserInfoRespVO getUserInfo(Long userId) { | ||||
|         MbrUserDO user = this.checkUserExists(userId); | ||||
|         // 拼接返回结果 | ||||
|         MbrUserInfoRespVO userResp = new MbrUserInfoRespVO(); | ||||
|         userResp.setNickName(user.getNickname()); | ||||
|         userResp.setAvatar(user.getAvatar()); | ||||
|         return userResp; | ||||
|         return UserProfileConvert.INSTANCE.convert(user); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) { | ||||
|         // 检测用户是否存在 | ||||
|         MbrUserDO userDO = checkUserExists(userId); | ||||
|         // 检测手机与验证码是否匹配 | ||||
|         // TODO @宋天:修改手机的时候。应该要校验,老手机 + 老手机 code;新手机 + 新手机 code | ||||
|         sysAuthService.checkIfMobileMatchCodeAndDeleteCode(userDO.getMobile(),reqVO.getCode()); | ||||
|         // 更新用户手机 | ||||
|         // TODO @宋天:更新的时候,单独创建对象。直接全量更新,会可能导致属性覆盖。可以看看打印出来的 SQL 哈 | ||||
|         userDO.setMobile(reqVO.getMobile()); | ||||
|         userMapper.updateById(userDO); | ||||
|     } | ||||
|  | ||||
|     @VisibleForTesting | ||||
|   | ||||
| @@ -3,7 +3,9 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth; | ||||
| import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService; | ||||
| import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||
| import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | ||||
| import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; | ||||
| import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; | ||||
| import com.alibaba.fastjson.JSON; | ||||
| @@ -38,7 +40,6 @@ public class SysAuthController { | ||||
|     @Resource | ||||
|     private SysSocialCoreService socialService; | ||||
|  | ||||
|  | ||||
|     @PostMapping("/login") | ||||
|     @ApiOperation("使用手机 + 密码登录") | ||||
|     public CommonResult<SysAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) { | ||||
| @@ -56,16 +57,49 @@ public class SysAuthController { | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/send-sms-code") | ||||
|     @ApiOperation("发送手机验证码") | ||||
|     @ApiOperation(value = "发送手机验证码",notes = "不检测该手机号是否已被注册") | ||||
|     public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) { | ||||
|         smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP()); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/send-sms-new-code") | ||||
|     @ApiOperation(value = "发送手机验证码",notes = "检测该手机号是否已被注册,用于修改手机时使用") | ||||
|     public CommonResult<Boolean> sendSmsNewCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) { | ||||
|         smsCodeService.sendSmsNewCode(reqVO); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
|     @GetMapping("/send-sms-code-login") | ||||
|     @ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用") | ||||
|     public CommonResult<Boolean> sendSmsCodeLogin() { | ||||
|         smsCodeService.sendSmsCodeLogin(getLoginUserId()); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/reset-password") | ||||
|     @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用") | ||||
|     @PreAuthenticated | ||||
|     public CommonResult<Boolean> resetPassword(@RequestBody @Valid MbrAuthResetPasswordReqVO reqVO) { | ||||
|         return null; | ||||
|         authService.resetPassword(reqVO); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/update-password") | ||||
|     @ApiOperation(value = "修改用户密码",notes = "用户修改密码时使用") | ||||
|     @PreAuthenticated | ||||
|     public CommonResult<Boolean> updatePassword(@RequestBody @Valid MbrAuthUpdatePasswordReqVO reqVO) { | ||||
|         authService.updatePassword(getLoginUserId(), reqVO); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/check-sms-code") | ||||
|     @ApiOperation(value = "校验验证码是否正确") | ||||
|     @PreAuthenticated | ||||
|     public CommonResult<Boolean> checkSmsCode(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) { | ||||
|         // TODO @宋天:check 的时候,不应该使用 useSmsCode 哈,这样验证码就直接被使用了。另外,check 开头的方法,更多是校验的逻辑,不会有 update 数据的动作。这点,在方法命名上,也是要注意的 | ||||
|         smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHECK_CODE_BY_SMS.getScene(),reqVO.getCode(),getClientIP()); | ||||
|         return success(true); | ||||
|     } | ||||
|  | ||||
|     // ========== 社交登录相关 ========== | ||||
|   | ||||
| @@ -0,0 +1,30 @@ | ||||
| package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; | ||||
|  | ||||
| import io.swagger.annotations.ApiModel; | ||||
| import io.swagger.annotations.ApiModelProperty; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.hibernate.validator.constraints.Length; | ||||
|  | ||||
| import javax.validation.constraints.NotBlank; | ||||
| import javax.validation.constraints.NotEmpty; | ||||
|  | ||||
| @ApiModel("修改密码 Request VO") | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| @Builder | ||||
| public class MbrAuthUpdatePasswordReqVO { | ||||
|  | ||||
|     @ApiModelProperty(value = "用户旧密码", required = true, example = "123456") | ||||
|     @NotBlank(message = "旧密码不能为空") | ||||
|     @Length(min = 4, max = 16, message = "密码长度为 4-16 位") | ||||
|     private String oldPassword; | ||||
|  | ||||
|     @ApiModelProperty(value = "新密码", required = true, example = "buzhidao") | ||||
|     @NotEmpty(message = "新密码不能为空") | ||||
|     @Length(min = 4, max = 16, message = "密码长度为 4-16 位") | ||||
|     private String password; | ||||
| } | ||||
| @@ -23,7 +23,10 @@ public interface SysErrorCodeConstants { | ||||
|     ErrorCode USER_SMS_CODE_NOT_CORRECT = new ErrorCode(1005001003, "验证码不正确"); | ||||
|     ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量"); | ||||
|     ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率"); | ||||
|     ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用"); | ||||
|  | ||||
|     // ========== 用户模块 1005002000 ========== | ||||
|     ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在"); | ||||
|     ErrorCode USER_CODE_FAILED = new ErrorCode(1005002002, "验证码不匹配"); | ||||
|     ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败"); | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,9 @@ public enum SysSmsSceneEnum implements IntArrayValuable { | ||||
|  | ||||
|     LOGIN_BY_SMS(1, "手机号登陆"), | ||||
|     CHANGE_MOBILE_BY_SMS(2, "更换手机号"), | ||||
|             ; | ||||
|     FORGET_MOBILE_BY_SMS(3, "忘记密码"), | ||||
|     CHECK_CODE_BY_SMS(4, "审核验证码"), | ||||
|     ; | ||||
|  | ||||
|     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray(); | ||||
|  | ||||
|   | ||||
| @@ -63,4 +63,23 @@ public interface SysAuthService extends SecurityAuthFrameworkService { | ||||
|      */ | ||||
|     void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO); | ||||
|  | ||||
|     /** | ||||
|      * 修改用户密码 | ||||
|      * @param userId 用户id | ||||
|      * @param userReqVO 用户请求实体类 | ||||
|      */ | ||||
|     void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO userReqVO); | ||||
|  | ||||
|     /** | ||||
|      * 忘记密码 | ||||
|      * @param userReqVO 用户请求实体类 | ||||
|      */ | ||||
|     void resetPassword(MbrAuthResetPasswordReqVO userReqVO); | ||||
|  | ||||
|     /** | ||||
|      * 检测手机与验证码是否匹配 | ||||
|      * @param phone 手机号 | ||||
|      * @param code 验证码 | ||||
|      */ | ||||
|     void checkIfMobileMatchCodeAndDeleteCode(String phone,String code); | ||||
| } | ||||
|   | ||||
| @@ -15,15 +15,18 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||||
| import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | ||||
| import cn.iocoder.yudao.framework.security.core.LoginUser; | ||||
| import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; | ||||
| import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; | ||||
| import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; | ||||
| import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert; | ||||
| import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; | ||||
| import com.google.common.annotations.VisibleForTesting; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import me.zhyd.oauth.model.AuthUser; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||||
| import org.springframework.security.authentication.AuthenticationManager; | ||||
| import org.springframework.security.authentication.BadCredentialsException; | ||||
| import org.springframework.security.authentication.DisabledException; | ||||
| @@ -32,14 +35,17 @@ import org.springframework.security.core.Authentication; | ||||
| import org.springframework.security.core.AuthenticationException; | ||||
| import org.springframework.security.core.userdetails.UserDetails; | ||||
| import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import javax.validation.Valid; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | ||||
| import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; | ||||
|  | ||||
| /** | ||||
| @@ -68,6 +74,15 @@ public class SysAuthServiceImpl implements SysAuthService { | ||||
|     @Resource | ||||
|     private SysSocialCoreService socialService; | ||||
|  | ||||
|     private SysSocialService socialService; | ||||
|     @Resource | ||||
|     private StringRedisTemplate stringRedisTemplate; | ||||
|     @Resource | ||||
|     private PasswordEncoder passwordEncoder; | ||||
|     @Resource | ||||
|     private MbrUserMapper userMapper; | ||||
|  | ||||
|     private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER; | ||||
|  | ||||
|     @Override | ||||
|     public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException { | ||||
| @@ -204,12 +219,12 @@ public class SysAuthServiceImpl implements SysAuthService { | ||||
|         } | ||||
|         reqDTO.setUsername(mobile); | ||||
|         reqDTO.setUserAgent(ServletUtils.getUserAgent()); | ||||
|         reqDTO.setUserIp(ServletUtils.getClientIP()); | ||||
|         reqDTO.setUserIp(getClientIP()); | ||||
|         reqDTO.setResult(loginResult.getResult()); | ||||
|         loginLogCoreService.createLoginLog(reqDTO); | ||||
|         // 更新最后登录时间 | ||||
|         if (user != null && Objects.equals(SysLoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | ||||
|             userService.updateUserLogin(user.getId(), ServletUtils.getClientIP()); | ||||
|             userService.updateUserLogin(user.getId(), getClientIP()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -270,6 +285,69 @@ public class SysAuthServiceImpl implements SysAuthService { | ||||
|         this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO reqVO) { | ||||
|         // 检验旧密码 | ||||
|         MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); | ||||
|  | ||||
|         // 更新用户密码 | ||||
|         // TODO @宋天:不要更新整个对象哈 | ||||
|         userDO.setPassword(passwordEncoder.encode(reqVO.getPassword())); | ||||
|         userMapper.updateById(userDO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void resetPassword(MbrAuthResetPasswordReqVO reqVO) { | ||||
|         // 根据验证码取出手机号,并查询用户 | ||||
|         String mobile = stringRedisTemplate.opsForValue().get(reqVO.getCode()); | ||||
|         MbrUserDO userDO = userMapper.selectByMobile(mobile); | ||||
|         if (userDO == null){ | ||||
|             throw exception(USER_NOT_EXISTS); | ||||
|         } | ||||
|         // TODO @芋艿 这一步没必要检验验证码与手机是否匹配,因为是根据验证码去redis中查找手机号,然后根据手机号查询用户 | ||||
|         //  也就是说 即便黑客以其他方式将验证码发送到自己手机上,最终还是会根据手机号查询用户然后进行重置密码的操作,不存在安全问题 | ||||
|  | ||||
|         // TODO @宋天:这块微信在讨论下哈~~~ | ||||
|  | ||||
|         // 校验验证码 | ||||
|         smsCodeService.useSmsCode(userDO.getMobile(), SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP()); | ||||
|  | ||||
|         // 更新密码 | ||||
|         userDO.setPassword(passwordEncoder.encode(reqVO.getPassword())); | ||||
|         userMapper.updateById(userDO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void checkIfMobileMatchCodeAndDeleteCode(String phone, String code) { | ||||
|         // 检验用户手机与验证码是否匹配 | ||||
|         String mobile = stringRedisTemplate.opsForValue().get(code); | ||||
|         if (!phone.equals(mobile)){ | ||||
|             throw exception(USER_CODE_FAILED); | ||||
|         } | ||||
|         // 销毁redis中此验证码 | ||||
|         stringRedisTemplate.delete(code); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验旧密码 | ||||
|      * | ||||
|      * @param id          用户 id | ||||
|      * @param oldPassword 旧密码 | ||||
|      * @return MbrUserDO 用户实体 | ||||
|      */ | ||||
|     @VisibleForTesting | ||||
|     public MbrUserDO checkOldPassword(Long id, String oldPassword) { | ||||
|         MbrUserDO user = userMapper.selectById(id); | ||||
|         if (user == null) { | ||||
|             throw exception(USER_NOT_EXISTS); | ||||
|         } | ||||
|         // 参数:未加密密码,编码后的密码 | ||||
|         if (!passwordEncoder.matches(oldPassword,user.getPassword())) { | ||||
|             throw exception(USER_PASSWORD_FAILED); | ||||
|         } | ||||
|         return user; | ||||
|     } | ||||
|  | ||||
|     private void createLogoutLog(Long userId, String username) { | ||||
|         SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); | ||||
|         reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); | ||||
| @@ -278,7 +356,7 @@ public class SysAuthServiceImpl implements SysAuthService { | ||||
|         reqDTO.setUserType(USER_TYPE_ENUM.getValue()); | ||||
|         reqDTO.setUsername(username); | ||||
|         reqDTO.setUserAgent(ServletUtils.getUserAgent()); | ||||
|         reqDTO.setUserIp(ServletUtils.getClientIP()); | ||||
|         reqDTO.setUserIp(getClientIP()); | ||||
|         reqDTO.setResult(SysLoginResultEnum.SUCCESS.getResult()); | ||||
|         loginLogCoreService.createLoginLog(reqDTO); | ||||
|     } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.userserver.modules.system.service.sms; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||
| import cn.iocoder.yudao.framework.common.validation.Mobile; | ||||
| import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO; | ||||
| import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||
|  | ||||
| /** | ||||
| @@ -20,6 +21,12 @@ public interface SysSmsCodeService { | ||||
|      */ | ||||
|     void sendSmsCode(@Mobile String mobile, Integer scene, String createIp); | ||||
|  | ||||
|     /** | ||||
|      * 发送短信验证码,并检测手机号是否已被注册 | ||||
|      * @param reqVO 请求实体 | ||||
|      */ | ||||
|     void sendSmsNewCode(SysAuthSendSmsReqVO reqVO); | ||||
|  | ||||
|     /** | ||||
|      * 验证短信验证码,并进行使用 | ||||
|      * 如果正确,则将验证码标记成已使用 | ||||
| @@ -32,4 +39,9 @@ public interface SysSmsCodeService { | ||||
|      */ | ||||
|     void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp); | ||||
|  | ||||
|     /** | ||||
|      * 根据用户id发送验证码 | ||||
|      * @param userId 用户id | ||||
|      */ | ||||
|     void sendSmsCodeLogin(Long userId); | ||||
| } | ||||
|   | ||||
| @@ -1,20 +1,27 @@ | ||||
| package cn.iocoder.yudao.userserver.modules.system.service.sms.impl; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService; | ||||
| import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; | ||||
| import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO; | ||||
| import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; | ||||
| import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper; | ||||
| import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||
| import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsTemplateCodeConstants; | ||||
| import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties; | ||||
| import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; | ||||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Date; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomInt; | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | ||||
| import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; | ||||
|  | ||||
| /** | ||||
| @@ -26,22 +33,52 @@ import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConst | ||||
| @Validated | ||||
| public class SysSmsCodeServiceImpl implements SysSmsCodeService { | ||||
|  | ||||
|     /** | ||||
|      * 验证码 + 手机 在redis中存储的有效时间,单位:分钟 | ||||
|      */ | ||||
|     private static final Long CODE_TIME = 10L; | ||||
|  | ||||
|     @Resource | ||||
|     private SmsCodeProperties smsCodeProperties; | ||||
|  | ||||
|     @Resource | ||||
|     private SysSmsCodeMapper smsCodeMapper; | ||||
|  | ||||
|     @Resource | ||||
|     private MbrUserService mbrUserService; | ||||
|  | ||||
|     @Resource | ||||
|     private SysSmsCoreService smsCoreService; | ||||
|  | ||||
|     @Resource | ||||
|     private StringRedisTemplate stringRedisTemplate; | ||||
|  | ||||
|     @Override | ||||
|     public void sendSmsCode(String mobile, Integer scene, String createIp) { | ||||
|         // 创建验证码 | ||||
|         String code = this.createSmsCode(mobile, scene, createIp); | ||||
|         // 发送验证码 | ||||
|         // TODO @宋天:这里可以拓展下 SysSmsSceneEnum,支持设置对应的短信模板编号(不同场景的短信文案是不同的)、是否要校验手机号已经注册。这样 Controller 就可以收口成一个接口了。相当于说,不同场景,不同策略 | ||||
|         smsCoreService.sendSingleSmsToMember(mobile, null, SysSmsTemplateCodeConstants.USER_SMS_LOGIN, | ||||
|                 MapUtil.of("code", code)); | ||||
|  | ||||
|         // 存储手机号与验证码到redis,用于标记 | ||||
|         // TODO @宋天:SysSmsCodeDO 表应该足够,无需增加额外的 redis 存储哇 | ||||
|         // TODO @宋天:Redis 相关的操作,不要散落到业务层,而是写一个它对应的 RedisDAO。这样,实现业务与技术的解耦 | ||||
|         // TODO @宋天:直接使用 code 作为 key,会存在 2 个问题:1)code 可能会冲突,多个手机号之间;2)缺少前缀。例如说 sms_code_${code} | ||||
|         stringRedisTemplate.opsForValue().set(code,mobile,CODE_TIME, TimeUnit.MINUTES); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void sendSmsNewCode(SysAuthSendSmsReqVO reqVO) { | ||||
|         // 检测手机号是否已被使用 | ||||
|         MbrUserDO userByMobile = mbrUserService.getUserByMobile(reqVO.getMobile()); | ||||
|         if (userByMobile != null){ | ||||
|             throw exception(USER_SMS_CODE_IS_EXISTS); | ||||
|         } | ||||
|  | ||||
|         // 发送短信 | ||||
|         this.sendSmsCode(reqVO.getMobile(),reqVO.getScene(),getClientIP()); | ||||
|     } | ||||
|  | ||||
|     private String createSmsCode(String mobile, Integer scene, String ip) { | ||||
| @@ -91,4 +128,14 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService { | ||||
|                 .used(true).usedTime(new Date()).usedIp(usedIp).build()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void sendSmsCodeLogin(Long userId) { | ||||
|         MbrUserDO user = mbrUserService.getUser(userId); | ||||
|         if (user == null){ | ||||
|             throw exception(USER_NOT_EXISTS); | ||||
|         } | ||||
|         // 发送验证码 | ||||
|         this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -63,6 +63,11 @@ spring: | ||||
|  | ||||
| --- #################### 定时任务相关配置 #################### | ||||
|  | ||||
| # Quartz 配置项,对应 QuartzProperties 配置类 | ||||
| spring: | ||||
|   quartz: | ||||
|     auto-startup: false # 无需使用 Quartz,交给 yudao-admin-server 环境 | ||||
|  | ||||
| --- #################### 配置中心相关配置 #################### | ||||
|  | ||||
| # Apollo 配置中心 | ||||
|   | ||||
| @@ -63,6 +63,11 @@ spring: | ||||
|  | ||||
| --- #################### 定时任务相关配置 #################### | ||||
|  | ||||
| # Quartz 配置项,对应 QuartzProperties 配置类 | ||||
| spring: | ||||
|   quartz: | ||||
|     auto-startup: false # 无需使用 Quartz,交给 yudao-admin-server 环境 | ||||
|  | ||||
| --- #################### 配置中心相关配置 #################### | ||||
|  | ||||
| # Apollo 配置中心 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV