diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 84f75d9..2617d3d 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -25,6 +25,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/water-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/water-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
index 62e69ea..1038d11 100644
--- a/water-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
+++ b/water-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
@@ -67,26 +67,32 @@ public class CaptchaController {
@RateLimiter(key = "#username", time = 60, count = 1)
@GetMapping("/resource/code")
public R code(@NotBlank(message = "{user.username.not.blank}") String username) {
- String key = GlobalConstants.CAPTCHA_CODE_KEY + username;
- String code = RandomUtil.randomNumbers(6);
- RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+ if (Validator.isMobile(username)) {
+ return sendSmsCode(username, username);
+ }
+ if (Validator.isEmail(username)) {
+ return sendEmailCode(username, username);
+ }
- if (Validator.isMobile(username)){
- // 验证码模板id 自行处理 (查数据库或写死均可)
- String templateId = "SMS_333877107";
- LinkedHashMap map = new LinkedHashMap<>(1);
- map.put("code", code);
- SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
- SmsResponse smsResponse = smsBlend.sendMessage(username, templateId, map);
- if (!smsResponse.isSuccess()) {
- log.error("验证码短信发送异常 => {}", smsResponse);
- return R.fail(smsResponse.getData().toString());
- }
- }else {
- emailCodeImpl(username);
- }
+ SysUserVo user = userService.selectUserByUserName(username);
+ if (user == null) {
+ return R.fail("账号未注册");
+ }
+ if (Validator.isMobile(user.getPhonenumber())) {
+ return sendSmsCode(user.getPhonenumber(), username);
+ }
+ if (Validator.isEmail(user.getEmail())) {
+ return sendEmailCode(user.getEmail(), username);
+ }
+ return R.fail("当前账号未绑定手机号或邮箱");
+ }
- return R.ok(code);
+ private R sendEmailCode(String email, String cacheKey) {
+ if (!mailProperties.getEnabled()) {
+ return R.fail("当前系统没有开启邮箱功能!");
+ }
+ emailCodeImpl(email, cacheKey);
+ return R.ok("操作成功");
}
diff --git a/water-admin/src/main/java/org/dromara/web/service/ForgotPasswordService.java b/water-admin/src/main/java/org/dromara/web/service/ForgotPasswordService.java
index e00d39e..f6f1457 100644
--- a/water-admin/src/main/java/org/dromara/web/service/ForgotPasswordService.java
+++ b/water-admin/src/main/java/org/dromara/web/service/ForgotPasswordService.java
@@ -1,20 +1,9 @@
package org.dromara.web.service;
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.crypto.digest.BCrypt;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
-import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.model.ForgotLoginBody;
-import org.dromara.common.core.exception.user.CaptchaExpireException;
-import org.dromara.common.core.exception.user.UserException;
-import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.redis.utils.RedisUtils;
-import org.dromara.system.domain.SysUser;
-import org.dromara.system.domain.vo.SysUserVo;
-import org.dromara.system.mapper.SysUserMapper;
+import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service;
@@ -22,41 +11,14 @@ import org.springframework.stereotype.Service;
@Service
public class ForgotPasswordService {
- private final SysUserMapper userMapper;
+ private final ISysUserService userService;
public boolean forgotPasswordService(ForgotLoginBody loginBody) {
- SysUserVo appUserVo =new SysUserVo();
- if(Validator.isEmail(loginBody.getUsername())){
- userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getEmail, loginBody.getUsername()));
- }else if (Validator.isMobile(loginBody.getUsername())){
- appUserVo = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, loginBody.getUsername()));
- }else {
- appUserVo = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, loginBody.getUsername()));
- }
- if (ObjectUtil.isNull(appUserVo)){
- throw new UserException("账号未注册");
- }
-
- boolean validateFlag = validateSmsCode(loginBody.getUsername(), loginBody.getSmsCode());
- if (!validateFlag){
- throw new UserException("验证码无效");
- }
-
- appUserVo.setPassword(BCrypt.hashpw(loginBody.getPassword()));
- SysUser update = MapstructUtils.convert(appUserVo, SysUser.class);
- userMapper.updateById(update);
- return userMapper.updateById(update) > 0;
- }
-
- /**
- * 校验短信验证码
- */
- private boolean validateSmsCode(String username, String smsCode) {
- String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + username);
- if (StringUtils.isBlank(code)) {
- throw new CaptchaExpireException();
- }
- return code.equals(smsCode);
+ String verificationCode = StringUtils.isNotBlank(loginBody.getSmsCode())
+ ? loginBody.getSmsCode()
+ : loginBody.getCode();
+ return userService.resetPasswordByVerificationCode(
+ loginBody.getUsername(), verificationCode, loginBody.getPassword());
}
}
diff --git a/water-admin/src/main/resources/application.yml b/water-admin/src/main/resources/application.yml
index 13fb8b2..6c8c966 100644
--- a/water-admin/src/main/resources/application.yml
+++ b/water-admin/src/main/resources/application.yml
@@ -172,6 +172,7 @@ security:
- /*/api-docs
- /*/api-docs/**
- /warm-flow-ui/config
+ - /app/v1/retrievePassword
# 多租户配置
tenant:
diff --git a/water-admin/src/test/java/org/dromara/web/config/SecurityExcludesConfigUnitTest.java b/water-admin/src/test/java/org/dromara/web/config/SecurityExcludesConfigUnitTest.java
new file mode 100644
index 0000000..34a370c
--- /dev/null
+++ b/water-admin/src/test/java/org/dromara/web/config/SecurityExcludesConfigUnitTest.java
@@ -0,0 +1,60 @@
+package org.dromara.web.config;
+
+import org.dromara.common.security.config.SecurityConfig;
+import org.dromara.common.security.config.properties.SecurityProperties;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
+import org.springframework.boot.env.YamlPropertySourceLoader;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.handler.MappedInterceptor;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Tag("dev")
+class SecurityExcludesConfigUnitTest {
+
+ @Test
+ void retrievePassword_isExcludedFromLoginAuthentication() throws Exception {
+ List> sources = new YamlPropertySourceLoader()
+ .load("application", new ClassPathResource("application.yml"));
+ MutablePropertySources propertySources = new MutablePropertySources();
+ for (PropertySource> source : sources) {
+ propertySources.addLast(source);
+ }
+ SecurityProperties properties = new Binder(ConfigurationPropertySources.from(propertySources))
+ .bind("security", Bindable.of(SecurityProperties.class))
+ .orElseThrow(() -> new AssertionError("security 配置未绑定"));
+
+ assertThat(properties.getExcludes()).contains("/app/v1/retrievePassword");
+ }
+
+ @Test
+ void securityInterceptor_alwaysExcludesRetrievePasswordWithoutExternalConfiguration() {
+ SecurityProperties properties = new SecurityProperties();
+ properties.setExcludes(new String[0]);
+ SecurityConfig config = new SecurityConfig(properties);
+ ReflectionTestUtils.setField(config, "ssePath", "/resource/sse");
+ ExposedInterceptorRegistry registry = new ExposedInterceptorRegistry();
+
+ config.addInterceptors(registry);
+
+ MappedInterceptor interceptor = (MappedInterceptor) registry.getRegisteredInterceptors().get(0);
+ assertThat(interceptor.getExcludePathPatterns()).contains("/app/v1/retrievePassword");
+ }
+
+ private static final class ExposedInterceptorRegistry extends InterceptorRegistry {
+
+ private List