From 08cfe71646aca782c59ca4977e79d17c758af0ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=AE=8B=E5=A4=A9?= <2982176321@qq.com>
Date: Sun, 19 Dec 2021 19:28:01 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=87=8D=E7=BD=AE=E6=89=8B?=
=?UTF-8?q?=E6=9C=BA=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yudao-user-server/pom.xml | 9 ---
.../user/SysUserProfileController.java | 7 --
.../user/vo/MbrUserUpdateMobileReqVO.java | 17 ++++-
.../service/user/impl/MbrUserServiceImpl.java | 29 +++++--
.../controller/auth/SysAuthController.java | 8 --
.../auth/vo/MbrAuthResetPasswordReqVO.java | 6 ++
.../auth/vo/SysAuthCheckCodeReqVO.java | 40 ++++++++++
.../dal/mysql/sms/SysSmsCodeMapper.java | 6 +-
.../system/enums/SysErrorCodeConstants.java | 2 +-
.../system/service/auth/SysAuthService.java | 9 +--
.../service/auth/impl/SysAuthServiceImpl.java | 51 ++++++-------
.../system/service/sms/SysSmsCodeService.java | 13 ++++
.../sms/impl/SysSmsCodeServiceImpl.java | 38 +++++-----
.../SysUserProfileControllerTest.java | 60 ---------------
.../controller/SysAuthControllerTest.java | 75 -------------------
15 files changed, 146 insertions(+), 224 deletions(-)
create mode 100644 yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthCheckCodeReqVO.java
delete mode 100644 yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java
delete mode 100644 yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java
diff --git a/yudao-user-server/pom.xml b/yudao-user-server/pom.xml
index 03dbfd699..be4005835 100644
--- a/yudao-user-server/pom.xml
+++ b/yudao-user-server/pom.xml
@@ -90,15 +90,6 @@
yudao-spring-boot-starter-test
test
-
-
-
-
- junit
- junit
- test
-
-
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java
index 93149e793..ae10719ce 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java
@@ -35,9 +35,6 @@ public class SysUserProfileController {
@Resource
private MbrUserService userService;
- @Resource
- private SysSmsCodeService smsCodeService;
-
@PutMapping("/update-nickname")
@ApiOperation("修改用户昵称")
@PreAuthenticated
@@ -68,10 +65,6 @@ public class SysUserProfileController {
@ApiOperation(value = "修改用户手机")
@PreAuthenticated
public CommonResult 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);
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java
index df1980b89..8593302d7 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
+import cn.iocoder.yudao.framework.common.validation.Mobile;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
@@ -27,9 +28,21 @@ public class MbrUserUpdateMobileReqVO {
@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 = "手机号格式错误")
+ @Mobile
private String mobile;
+ @ApiModelProperty(value = "原手机验证码", required = true, example = "1024")
+ @NotEmpty(message = "原手机验证码不能为空")
+ @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
+ @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
+ private String oldCode;
+
+ @ApiModelProperty(value = "原手机号",required = true,example = "15823654487")
+ @NotBlank(message = "手机号不能为空")
+ @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
+ @Mobile
+ private String oldMobile;
+
+
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
index f45ad257b..acfc489de 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
@@ -9,7 +9,10 @@ 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.dal.mysql.user.MbrUserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
+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 org.springframework.security.crypto.password.PasswordEncoder;
@@ -21,7 +24,10 @@ import java.io.InputStream;
import java.util.Date;
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.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS;
+import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED;
+import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.USER_SMS_CODE_NOT_CORRECT;
/**
* User Service 实现类
@@ -45,6 +51,10 @@ public class MbrUserServiceImpl implements MbrUserService {
@Resource
private SysAuthService sysAuthService;
+ @Resource
+ private SysSmsCodeService smsCodeService;
+
+
@Override
public MbrUserDO getUserByMobile(String mobile) {
return userMapper.selectByMobile(mobile);
@@ -124,12 +134,21 @@ public class MbrUserServiceImpl implements MbrUserService {
@Override
public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) {
// 检测用户是否存在
- MbrUserDO userDO = checkUserExists(userId);
- // 检测手机与验证码是否匹配
- // TODO @宋天:修改手机的时候。应该要校验,老手机 + 老手机 code;新手机 + 新手机 code
- sysAuthService.checkIfMobileMatchCodeAndDeleteCode(userDO.getMobile(),reqVO.getCode());
+ checkUserExists(userId);
+ // 校验验证码,并标记为已使用
+ smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
+
+ // 检测新手机和旧手机的验证码是否在30分钟内
+ SysSmsCodeDO smsOldCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
+ SysSmsCodeDO smsNewCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getMobile(), reqVO.getCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
+
+ // 判断新旧code是否未被使用,如果是,抛出异常
+ if (Boolean.FALSE.equals(smsOldCodeDO.getUsed()) || Boolean.FALSE.equals(smsNewCodeDO.getUsed())){
+ throw exception(USER_SMS_CODE_IS_UNUSED);
+ }
+
// 更新用户手机
- // TODO @宋天:更新的时候,单独创建对象。直接全量更新,会可能导致属性覆盖。可以看看打印出来的 SQL 哈
+ MbrUserDO userDO = MbrUserDO.builder().build();
userDO.setMobile(reqVO.getMobile());
userMapper.updateById(userDO);
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
index 9e6b9f450..dcc98e402 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
@@ -93,14 +93,6 @@ public class SysAuthController {
return success(true);
}
- @PostMapping("/check-sms-code")
- @ApiOperation(value = "校验验证码是否正确")
- @PreAuthenticated
- public CommonResult 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);
- }
// ========== 社交登录相关 ==========
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
index 3b3556fdb..816017455 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
+import cn.iocoder.yudao.framework.common.validation.Mobile;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
@@ -8,6 +9,7 @@ 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;
@@ -29,4 +31,8 @@ public class MbrAuthResetPasswordReqVO {
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
private String code;
+ @ApiModelProperty(value = "手机号",required = true,example = "15878962356")
+ @NotBlank(message = "手机号不能为空")
+ @Mobile
+ private String mobile;
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthCheckCodeReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthCheckCodeReqVO.java
new file mode 100644
index 000000000..89f1f501e
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthCheckCodeReqVO.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.framework.common.validation.Mobile;
+import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
+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.NotNull;
+import javax.validation.constraints.Pattern;
+
+@ApiModel("校验验证码 Request VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class SysAuthCheckCodeReqVO {
+
+ @ApiModelProperty(value = "手机号", example = "15601691234")
+ @NotBlank(message = "手机号不能为空")
+ @Mobile
+ private String mobile;
+
+ @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
+ @NotBlank(message = "手机验证码不能为空")
+ @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
+ @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
+ private String code;
+
+ @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举")
+ @NotNull(message = "发送场景不能为空")
+ @InEnum(SysSmsSceneEnum.class)
+ private Integer scene;
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java
index 6ef96ca1f..88e10363f 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
import org.apache.ibatis.annotations.Mapper;
+
@Mapper
public interface SysSmsCodeMapper extends BaseMapperX {
@@ -13,14 +14,15 @@ public interface SysSmsCodeMapper extends BaseMapperX {
*
* @param mobile 手机号
* @param scene 发送场景,选填
+ * @param code 验证码 选填
* @return 手机验证码
*/
- default SysSmsCodeDO selectLastByMobile(String mobile, Integer scene) {
+ default SysSmsCodeDO selectLastByMobile(String mobile,String code,Integer scene) {
return selectOne(new QueryWrapperX()
.eq("mobile", mobile)
.eqIfPresent("scene", scene)
+ .eqIfPresent("code", code)
.orderByDesc("id")
.last("LIMIT 1"));
}
-
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java
index e7c104afb..c37dca9b0 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java
@@ -24,9 +24,9 @@ public interface SysErrorCodeConstants {
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, "手机号已被使用");
+ ErrorCode USER_SMS_CODE_IS_UNUSED = 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, "密码校验失败");
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java
index 33664d351..62cb2e716 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java
@@ -68,18 +68,11 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
* @param userId 用户id
* @param userReqVO 用户请求实体类
*/
- void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO userReqVO);
+ void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO userReqVO);
/**
* 忘记密码
* @param userReqVO 用户请求实体类
*/
void resetPassword(MbrAuthResetPasswordReqVO userReqVO);
-
- /**
- * 检测手机与验证码是否匹配
- * @param phone 手机号
- * @param code 验证码
- */
- void checkIfMobileMatchCodeAndDeleteCode(String phone,String code);
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
index f4193b35b..756f50f22 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
@@ -280,46 +280,30 @@ public class SysAuthServiceImpl implements SysAuthService {
}
@Override
- public void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO reqVO) {
+ public void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO reqVO) {
// 检验旧密码
MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword());
// 更新用户密码
- // TODO @宋天:不要更新整个对象哈
- userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
- userMapper.updateById(userDO);
+ MbrUserDO mbrUserDO = MbrUserDO.builder().build();
+ mbrUserDO.setId(userDO.getId());
+ mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
+ userMapper.updateById(mbrUserDO);
}
@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中查找手机号,然后根据手机号查询用户
- // 也就是说 即便黑客以其他方式将验证码发送到自己手机上,最终还是会根据手机号查询用户然后进行重置密码的操作,不存在安全问题
+ // 检验用户是否存在
+ MbrUserDO userDO = checkUserIfExists(reqVO.getMobile());
- // TODO @宋天:这块微信在讨论下哈~~~
-
- // 校验验证码
- smsCodeService.useSmsCode(userDO.getMobile(), SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
+ // 使用验证码
+ smsCodeService.useSmsCode(reqVO.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);
+ MbrUserDO mbrUserDO = MbrUserDO.builder().build();
+ mbrUserDO.setId(userDO.getId());
+ mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
+ userMapper.updateById(mbrUserDO);
}
/**
@@ -342,6 +326,15 @@ public class SysAuthServiceImpl implements SysAuthService {
return user;
}
+ public MbrUserDO checkUserIfExists(String mobile) {
+ MbrUserDO user = userMapper.selectByMobile(mobile);
+ if (user == null) {
+ throw exception(USER_NOT_EXISTS);
+ }
+ return user;
+ }
+
+
private void createLogoutLog(Long userId, String username) {
SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java
index 6e9c3c7b3..160527d35 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java
@@ -2,7 +2,10 @@ 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.SysAuthCheckCodeReqVO;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;
+import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
/**
@@ -44,4 +47,14 @@ public interface SysSmsCodeService {
* @param userId 用户id
*/
void sendSmsCodeLogin(Long userId);
+
+ /**
+ * 检测手机验证码是否有效
+ * @param mobile 手机号
+ * @param code 验证码
+ * @param scene 发送场景 {@link SysSmsSceneEnum}
+ * @return SysSmsCodeDO 手机验证码
+ */
+ SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene);
+
}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java
index a5796c6fc..c7aa98f91 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java
@@ -4,6 +4,7 @@ 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.SysAuthCheckCodeReqVO;
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;
@@ -11,13 +12,11 @@ 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;
@@ -33,11 +32,6 @@ 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;
@@ -50,9 +44,6 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
@Resource
private SysSmsCoreService smsCoreService;
- @Resource
- private StringRedisTemplate stringRedisTemplate;
-
@Override
public void sendSmsCode(String mobile, Integer scene, String createIp) {
// 创建验证码
@@ -61,12 +52,6 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
// 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
@@ -83,7 +68,7 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
private String createSmsCode(String mobile, Integer scene, String ip) {
// 校验是否可以发送验证码,不用筛选场景
- SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null);
+ SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null);
if (lastSmsCode != null) {
if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
@@ -108,7 +93,7 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
@Override
public void useSmsCode(String mobile, Integer scene, String code, String usedIp) {
// 校验验证码
- SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, scene);
+ SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,scene);
if (lastSmsCode == null) { // 若验证码不存在,抛出异常
throw exception(USER_SMS_CODE_NOT_FOUND);
}
@@ -138,4 +123,21 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP());
}
+ @Override
+ public SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene) {
+ SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene);
+
+ // 检测验证码是否存在
+ if (lastSmsCode == null){
+ throw exception(USER_SMS_CODE_EXPIRED);
+ }
+
+ // 检测验证码是否过期
+ if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
+ >= smsCodeProperties.getExpireTimes().toMillis()) {
+ throw exception(USER_SMS_CODE_EXPIRED);
+ }
+ return lastSmsCode;
+ }
+
}
diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java b/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java
deleted file mode 100644
index 3f32cc82c..000000000
--- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package cn.iocoder.yudao.userserver.modules.member.controller;
-
-import cn.iocoder.yudao.userserver.modules.member.controller.user.SysUserProfileController;
-import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
-import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-/**
- * {@link SysUserProfileController} 的单元测试类
- *
- * @author 宋天
- */
-// TODO @宋天:controller 的单测可以不写哈,因为收益太低了。未来我们做 qa 自动化测试
-public class SysUserProfileControllerTest {
-
- private MockMvc mockMvc;
-
- @InjectMocks
- private SysUserProfileController sysUserProfileController;
-
- @Mock
- private MbrUserService userService;
-
- @Mock
- private SysSmsCodeService smsCodeService;
-
- @Before // TODO @宋天:使用 junit5 哈
- public void setup() {
- // 初始化
- MockitoAnnotations.openMocks(this);
-
- // 构建mvc环境
- mockMvc = MockMvcBuilders.standaloneSetup(sysUserProfileController).build();
- }
-
- @Test
- public void testUpdateMobile_success() throws Exception {
- //模拟接口调用
- this.mockMvc.perform(post("/system/user/profile/update-mobile")
- .contentType(MediaType.APPLICATION_JSON_VALUE)
- .content("{\"mobile\":\"15819844280\",\"code\":\"123456\"}}"))
- .andExpect(status().isOk())
- .andDo(MockMvcResultHandlers.print());
-// TODO @宋天:方法的结尾,不用空行哈
- }
-
-
-}
diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java b/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java
deleted file mode 100644
index 599ebaab6..000000000
--- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package cn.iocoder.yudao.userserver.modules.system.controller;
-
-import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
-import cn.iocoder.yudao.userserver.modules.system.controller.auth.SysAuthController;
-import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
-import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-
-import static org.springframework.http.HttpHeaders.AUTHORIZATION;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-/**
- * {@link SysAuthController} 的单元测试类
- *
- * @author 宋天
- */
-public class SysAuthControllerTest {
-
- private MockMvc mockMvc;
-
- @InjectMocks
- private SysAuthController sysAuthController;
-
- @Mock
- private SysAuthService authService;
- @Mock
- private SysSmsCodeService smsCodeService;
- @Mock
- private SysSocialService socialService;
-
-
- @Before
- public void setup() {
- // 初始化
- MockitoAnnotations.openMocks(this);
-
- // 构建mvc环境
- mockMvc = MockMvcBuilders.standaloneSetup(sysAuthController).build();
- }
-
- @Test
- public void testResetPassword_success() throws Exception {
- //模拟接口调用
- this.mockMvc.perform(post("/reset-password")
- .contentType(MediaType.APPLICATION_JSON)
- .content("{\"password\":\"1123\",\"code\":\"123456\"}}"))
- .andExpect(status().isOk())
- .andDo(MockMvcResultHandlers.print());
-
- }
-
- @Test
- public void testUpdatePassword_success() throws Exception {
- //模拟接口调用
- this.mockMvc.perform(post("/update-password")
- .contentType(MediaType.APPLICATION_JSON)
- .content("{\"password\":\"1123\",\"code\":\"123456\",\"oldPassword\":\"1123\"}}"))
- .andExpect(status().isOk())
- .andDo(MockMvcResultHandlers.print());
-
- }
-
-
-
-}