mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
feat(SysUserServiceImpl): 添加短信验证码校验逻辑,防止重复使用确保验证码一次性消费
- 更新用户注册和找回密码逻辑,使用新的验证码校验方法 - 添加 SysUserSmsCodeTests 测试类,验证验证码消费行为
This commit is contained in:
1 parent
fd5cd572a4
commit
db23fd2d0c
2 files changed
+99
-9
No files matched your search
+17
-9
@@ -49,6 +49,7 @@ import com.pig4cloud.pigx.common.data.datascope.DataScope;
|
||||
import com.pig4cloud.pigx.common.data.resolver.ParamResolver;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
|
||||
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
|
||||
import com.pig4cloud.pigx.common.security.captcha.CaptchaValidator;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUser;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -110,6 +111,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
|
||||
private final SysDeptMapper sysDeptMapper;
|
||||
|
||||
private final CaptchaValidator captchaValidator;
|
||||
|
||||
/**
|
||||
* 保存用户信息
|
||||
*
|
||||
@@ -332,9 +335,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
|
||||
// 校验手机号
|
||||
if (StrUtil.isNotBlank(userDto.getPhone())) {
|
||||
Object codeObj = RedisUtils.get(CacheConstants.SMS_CODE_KEY + LoginTypeEnum.SMS.getType()
|
||||
+ StringPool.AT + userDto.getPhone());
|
||||
if (!StrUtil.equals(userDto.getCode(), codeObj.toString())) {
|
||||
if (!validateSmsCode(userDto.getPhone(), userDto.getCode())) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR));
|
||||
}
|
||||
sysUser.setPhone(userDto.getPhone());
|
||||
@@ -607,9 +608,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
if (StrUtil.isBlank(userDto.getCode())) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_ILLEGAL));
|
||||
}
|
||||
String codeObj = RedisUtils
|
||||
.get(CacheConstants.SMS_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone());
|
||||
if (!StrUtil.equals(codeObj, userDto.getCode())) {
|
||||
if (!validateSmsCode(userDto.getPhone(), userDto.getCode())) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR));
|
||||
}
|
||||
}
|
||||
@@ -772,9 +771,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_ILLEGAL));
|
||||
}
|
||||
|
||||
String codeObj = RedisUtils
|
||||
.get(CacheConstants.SMS_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone());
|
||||
if (!StrUtil.equals(codeObj, code)) {
|
||||
if (!validateSmsCode(userDto.getPhone(), code)) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR));
|
||||
}
|
||||
|
||||
@@ -798,6 +795,17 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验短信验证码,读取即作废,防止同一验证码被重复尝试爆破
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @param code 短信验证码
|
||||
* @return 校验通过返回 true
|
||||
*/
|
||||
private boolean validateSmsCode(String phone, String code) {
|
||||
return captchaValidator.validateSms(LoginTypeEnum.SMS.getType() + StringPool.AT + phone, code).isOk();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色ID列表获取用户ID列表
|
||||
*
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package com.pig4cloud.pigx.admin.service.impl;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.dto.RegisterUserDTO;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import com.pig4cloud.pigx.common.security.captcha.CaptchaValidator;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 短信验证码必须一次性消费,防止 4 位验证码被重复尝试爆破。
|
||||
*/
|
||||
class SysUserSmsCodeTests {
|
||||
|
||||
private static final String KEY = "SMS_CODE_KEY:SMS@13800138000";
|
||||
|
||||
private final Map<String, Object> cache = new HashMap<>();
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
new SpringContextHolder().postProcessBeanFactory(null);
|
||||
SpringContextHolder.clearHolder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrongCodeConsumesSmsCodeForRegisterAndForgetPassword() {
|
||||
SysUserServiceImpl service = newService();
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
|
||||
|
||||
cache.put(KEY, "1234");
|
||||
assertThat(service.registerUser(dto("0000")).isOk()).isFalse();
|
||||
// 猜错一次后验证码已作废,正确的码也不能再用
|
||||
assertThat(service.registerUser(dto("1234")).isOk()).isFalse();
|
||||
assertThat(cache).doesNotContainKey(KEY);
|
||||
|
||||
cache.put(KEY, "1234");
|
||||
assertThat(service.forgetUserPassword(dto(null), "0000").isOk()).isFalse();
|
||||
assertThat(service.forgetUserPassword(dto(null), "1234").isOk()).isFalse();
|
||||
assertThat(cache).doesNotContainKey(KEY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private SysUserServiceImpl newService() {
|
||||
RedisTemplate<String, Object> redis = mock(RedisTemplate.class);
|
||||
ValueOperations<String, Object> values = mock(ValueOperations.class);
|
||||
when(redis.opsForValue()).thenReturn(values);
|
||||
when(values.getAndDelete(anyString())).thenAnswer(invocation -> cache.remove(invocation.getArgument(0)));
|
||||
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
StaticMessageSource messages = new StaticMessageSource();
|
||||
messages.setUseCodeAsDefaultMessage(true);
|
||||
factory.registerSingleton("pigxMessageSource", messages);
|
||||
new SpringContextHolder().postProcessBeanFactory(factory);
|
||||
|
||||
return new SysUserServiceImpl(null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
new CaptchaValidator(null, redis));
|
||||
}
|
||||
|
||||
private RegisterUserDTO dto(String code) {
|
||||
RegisterUserDTO dto = new RegisterUserDTO();
|
||||
dto.setUsername("attacker");
|
||||
dto.setPhone("13800138000");
|
||||
dto.setCode(code);
|
||||
return dto;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user