templateParams = new HashMap<>();
+ templateParams.put("code", code);
+ try {
+ smsService.sendSms(mobile, SmsTypeEnum.LOGIN, templateParams);
+ } catch (Exception e) {
+ log.error("发送短信验证码失败", e);
+ }
+ // 缓存验证码至Redis,用于登录校验
+ redisTemplate.opsForValue().set(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile, code, 5, TimeUnit.MINUTES);
+ }
+
+ /**
+ * 短信验证码登录
+ *
+ * @param mobile 手机号
+ * @param code 验证码
+ * @return 访问令牌
+ */
+ @Override
+ public AuthenticationToken loginBySms(String mobile, String code) {
+ // 1. 创建用户微信认证的令牌(未认证)
+ SmsAuthenticationToken smsAuthenticationToken = new SmsAuthenticationToken(mobile, code);
+
+ // 2. 执行认证(认证中)
+ Authentication authentication = authenticationManager.authenticate(smsAuthenticationToken);
+
+ // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
+ AuthenticationToken authenticationToken = tokenManager.generateToken(authentication);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ return authenticationToken;
+ }
+
+ /**
+ * 注销登录
*/
@Override
public void logout() {
@@ -99,7 +156,7 @@ public class AuthServiceImpl implements AuthService {
if (StrUtil.isNotBlank(token) && token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) {
token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
// 将JWT令牌加入黑名单
- tokenService.blacklistToken(token);
+ tokenManager.blacklistToken(token);
// 清除Security上下文
SecurityContextHolder.clearContext();
}
@@ -111,7 +168,7 @@ public class AuthServiceImpl implements AuthService {
* @return 验证码
*/
@Override
- public CaptchaResponse getCaptcha() {
+ public CaptchaInfo getCaptcha() {
String captchaType = captchaProperties.getType();
int width = captchaProperties.getWidth();
@@ -143,31 +200,28 @@ public class AuthServiceImpl implements AuthService {
redisTemplate.opsForValue().set(SecurityConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaCode,
captchaProperties.getExpireSeconds(), TimeUnit.SECONDS);
- return CaptchaResponse.builder()
+ return CaptchaInfo.builder()
.captchaKey(captchaKey)
.captchaBase64(imageBase64Data)
.build();
}
/**
- * 刷新令牌
+ * 刷新token
*
- * @param request 刷新令牌请求参数
+ * @param refreshToken 刷新令牌
* @return 新的访问令牌
*/
@Override
- public AuthTokenResponse refreshToken(RefreshTokenRequest request) {
+ public AuthenticationToken refreshToken(String refreshToken) {
// 验证刷新令牌
-
- String refreshToken = request.getRefreshToken();
-
- boolean isValidate = tokenService.validateToken(refreshToken);
+ boolean isValidate = tokenManager.validateToken(refreshToken);
if (!isValidate) {
throw new BusinessException(ResultCode.REFRESH_TOKEN_INVALID);
}
-
- return tokenService.refreshToken(refreshToken);
+ // 刷新令牌有效,生成新的访问令牌
+ return tokenManager.refreshToken(refreshToken);
}
diff --git a/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java b/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java
index e5ea9f2f..db5fce55 100644
--- a/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java
+++ b/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java
@@ -128,8 +128,8 @@ public class MinioFileService implements FileService {
/**
* 删除文件
*
- * @param filePath 文件路径
- * https://oss.youlai.tech/default/20221120/test.jpg
+ * @param filePath 文件路径 http://localhost:9000/default/20221120/test.jpg
+ *
* @return
*/
@Override
@@ -151,10 +151,8 @@ public class MinioFileService implements FileService {
minioClient.removeObject(removeObjectArgs);
return true;
- } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
- InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
- XmlParserException e) {
- throw new RuntimeException(e);
+ } catch (Exception e) {
+ throw new RuntimeException("文件删除失败", e);
}
}
diff --git a/src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java b/src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java
index bb85859d..710601bc 100644
--- a/src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java
+++ b/src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java
@@ -6,6 +6,7 @@ package com.youlai.boot.shared.sms.controller;
* @author Ray
* @since 2.10.0
*/
+
public class SmsController {
diff --git a/src/main/java/com/youlai/boot/shared/sms/enums/SmsTypeEnum.java b/src/main/java/com/youlai/boot/shared/sms/enums/SmsTypeEnum.java
new file mode 100644
index 00000000..b852a797
--- /dev/null
+++ b/src/main/java/com/youlai/boot/shared/sms/enums/SmsTypeEnum.java
@@ -0,0 +1,39 @@
+package com.youlai.boot.shared.sms.enums;
+
+import com.youlai.boot.common.base.IBaseEnum;
+import lombok.Getter;
+
+/**
+ * 短信类型枚举
+ *
+ * value 值对应 application-*.yml 中的 sms.templates.* 配置
+ *
+ * @author Ray.Hao
+ * @since 2.21.0
+ */
+@Getter
+public enum SmsTypeEnum implements IBaseEnum {
+
+ /**
+ * 注册短信验证码
+ */
+ REGISTER("register", "注册短信验证码"),
+
+ /**
+ * 登录短信验证码
+ */
+ LOGIN("login", "登录短信验证码"),
+
+ /**
+ * 修改手机号短信验证码
+ */
+ CHANGE_MOBILE("change-mobile", "修改手机号短信验证码");
+
+ private final String value;
+ private final String label;
+
+ SmsTypeEnum(String value, String label) {
+ this.value = value;
+ this.label = label;
+ }
+}
diff --git a/src/main/java/com/youlai/boot/shared/sms/service/SmsService.java b/src/main/java/com/youlai/boot/shared/sms/service/SmsService.java
index 85583d07..3deb1157 100644
--- a/src/main/java/com/youlai/boot/shared/sms/service/SmsService.java
+++ b/src/main/java/com/youlai/boot/shared/sms/service/SmsService.java
@@ -1,11 +1,13 @@
package com.youlai.boot.shared.sms.service;
+import com.youlai.boot.shared.sms.enums.SmsTypeEnum;
+
+import java.util.Map;
+
/**
* 短信服务接口层
- *
- * SMS = Short Message Service 短信服务
*
- * @author Ray
+ * @author Ray.Hao
* @since 2024/8/17
*/
public interface SmsService {
@@ -13,10 +15,10 @@ public interface SmsService {
/**
* 发送短信
*
- * @param mobile 手机号 13388886666
- * @param templateCode 短信模板 SMS_194640010
- * @param templateParam 模板参数 "[{"code":"123456"}]"
+ * @param mobile 手机号 13388886666
+ * @param smsType 短信模板 SMS_194640010,模板内容:您的验证码为:${code},请在5分钟内使用
+ * @param templateParams 模板参数 [{"code":"123456"}] ,用于替换短信模板中的变量
* @return boolean 是否发送成功
*/
- boolean sendSms(String mobile, String templateCode, String templateParam);
+ boolean sendSms(String mobile, SmsTypeEnum smsType, Map templateParams);
}
diff --git a/src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java b/src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java
index a831bdf3..b37ed844 100644
--- a/src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java
+++ b/src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java
@@ -1,23 +1,26 @@
package com.youlai.boot.shared.sms.service.impl;
+import cn.hutool.json.JSONUtil;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
-import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.youlai.boot.config.property.AliyunSmsProperties;
+import com.youlai.boot.shared.sms.enums.SmsTypeEnum;
import com.youlai.boot.shared.sms.service.SmsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
+import java.util.Map;
+
/**
* 阿里云短信业务类
- *
+ *
* @author Ray
- * @since 2024/8/17
+ * @since 2024/8/17
*/
@Service
@RequiredArgsConstructor
@@ -28,14 +31,15 @@ public class AliyunSmsService implements SmsService {
/**
* 发送短信验证码
*
- * @param mobile 手机号 13388886666
- * @param templateCode 短信模板 SMS_194640010
- * @param templateParam 模板参数 "[{"code":"123456"}]"
- *
- * @return boolean 是否发送成功
+ * @param mobile 手机号 13388886666
+ * @param smsType 短信模板 SMS_194640010
+ * @param templateParams 模板参数 [{"code":"123456"}]
+ * @return boolean 是否发送成功
*/
@Override
- public boolean sendSms(String mobile,String templateCode,String templateParam) {
+ public boolean sendSms(String mobile, SmsTypeEnum smsType, Map templateParams) {
+
+ String templateCode = aliyunSmsProperties.getTemplates().get(smsType.getValue());
DefaultProfile profile = DefaultProfile.getProfile(aliyunSmsProperties.getRegionId(),
aliyunSmsProperties.getAccessKeyId(), aliyunSmsProperties.getAccessKeySecret());
@@ -60,13 +64,11 @@ public class AliyunSmsService implements SmsService {
// 您申请的模板 code
request.putQueryParameter("TemplateCode", templateCode);
- request.putQueryParameter("TemplateParam", templateParam);
+ request.putQueryParameter("TemplateParam", JSONUtil.toJsonStr(templateParams));
try {
CommonResponse response = client.getCommonResponse(request);
return response.getHttpResponse().isSuccess();
- } catch (ServerException e) {
- e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}
diff --git a/src/main/java/com/youlai/boot/system/controller/RoleController.java b/src/main/java/com/youlai/boot/system/controller/RoleController.java
index 54cdcf33..b5b7419a 100644
--- a/src/main/java/com/youlai/boot/system/controller/RoleController.java
+++ b/src/main/java/com/youlai/boot/system/controller/RoleController.java
@@ -22,11 +22,10 @@ import jakarta.validation.Valid;
import java.util.List;
-
/**
* 角色控制层
*
- * @author Ray
+ * @author Ray.Hao
* @since 2022/10/16
*/
@Tag(name = "03.角色接口")
@@ -39,9 +38,9 @@ public class RoleController {
@Operation(summary = "角色分页列表")
@GetMapping("/page")
- @Log( value = "角色分页列表",module = LogModuleEnum.ROLE)
+ @Log(value = "角色分页列表", module = LogModuleEnum.ROLE)
public PageResult getRolePage(
- RolePageQuery queryParams
+ RolePageQuery queryParams
) {
Page result = roleService.getRolePage(queryParams);
return PageResult.success(result);
@@ -83,11 +82,11 @@ public class RoleController {
@Operation(summary = "删除角色")
@DeleteMapping("/{ids}")
@PreAuthorize("@ss.hasPerm('sys:role:delete')")
- public Result> deleteRoles(
+ public Result deleteRoles(
@Parameter(description = "删除角色,多个以英文逗号(,)拼接") @PathVariable String ids
) {
- boolean result = roleService.deleteRoles(ids);
- return Result.judge(result);
+ roleService.deleteRoles(ids);
+ return Result.success();
}
@Operation(summary = "修改角色状态")
@@ -111,11 +110,11 @@ public class RoleController {
@Operation(summary = "分配菜单(包括按钮权限)给角色")
@PutMapping("/{roleId}/menus")
- public Result> assignMenusToRole(
+ public Result assignMenusToRole(
@PathVariable Long roleId,
@RequestBody List menuIds
) {
- boolean result = roleService.assignMenusToRole(roleId, menuIds);
- return Result.judge(result);
+ roleService.assignMenusToRole(roleId, menuIds);
+ return Result.success();
}
}
diff --git a/src/main/java/com/youlai/boot/system/controller/UserController.java b/src/main/java/com/youlai/boot/system/controller/UserController.java
index c425d14a..88af5819 100644
--- a/src/main/java/com/youlai/boot/system/controller/UserController.java
+++ b/src/main/java/com/youlai/boot/system/controller/UserController.java
@@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youlai.boot.common.annotation.Log;
import com.youlai.boot.common.annotation.RepeatSubmit;
-import com.youlai.boot.system.enums.ContactType;
import com.youlai.boot.common.enums.LogModuleEnum;
import com.youlai.boot.common.model.Option;
import com.youlai.boot.common.result.PageResult;
@@ -45,7 +44,7 @@ import java.util.List;
/**
* 用户控制层
*
- * @author Ray
+ * @author Ray.Hao
* @since 2022/10/16
*/
@Tag(name = "02.用户接口")
@@ -203,39 +202,46 @@ public class UserController {
@Operation(summary = "修改密码")
@PutMapping(value = "/password")
public Result> changePassword(
- @RequestBody PasswordChangeForm data
+ @RequestBody PasswordUpdateForm data
) {
Long currUserId = SecurityUtils.getUserId();
boolean result = userService.changePassword(currUserId, data);
return Result.judge(result);
}
- @Operation(summary = "发送短信/邮箱验证码")
- @PostMapping(value = "/send-verification-code")
- public Result> sendVerificationCode(
- @Parameter(description = "联系方式(手机号码或邮箱地址)", required = true) @RequestParam String contact,
- @Parameter(description = "联系方式类型(Mobile或Email)", required = true) @RequestParam ContactType contactType
+ @Operation(summary = "发送短信验证码(绑定或更换手机号)")
+ @PostMapping(value = "/mobile/code")
+ public Result> sendMobileCode(
+ @Parameter(description = "手机号码", required = true) @RequestParam String mobile
) {
- boolean result = userService.sendVerificationCode(contact, contactType);
+ boolean result = userService.sendMobileCode(mobile);
return Result.judge(result);
}
- @Operation(summary = "个人中心绑定用户手机号")
+ @Operation(summary = "绑定或更换手机号")
@PutMapping(value = "/mobile")
- public Result> bindMobile(
- @RequestBody @Validated MobileBindingForm data
+ public Result> bindOrChangeMobile(
+ @RequestBody @Validated MobileUpdateForm data
) {
- boolean result = userService.bindMobile(data);
+ boolean result = userService.bindOrChangeMobile(data);
return Result.judge(result);
}
-
- @Operation(summary = "个人中心绑定用户邮箱")
- @PutMapping(value = "/email")
- public Result> bindEmail(
- @RequestBody @Validated EmailBindingForm data
+ @Operation(summary = "发送邮箱验证码(绑定或更换邮箱)")
+ @PostMapping(value = "/email/code")
+ public Result sendEmailCode(
+ @Parameter(description = "邮箱地址", required = true) @RequestParam String email
) {
- boolean result = userService.bindEmail(data);
+ userService.sendEmailCode(email);
+ return Result.success();
+ }
+
+ @Operation(summary = "绑定或更换邮箱")
+ @PutMapping(value = "/email")
+ public Result> bindOrChangeEmail(
+ @RequestBody @Validated EmailUpdateForm data
+ ) {
+ boolean result = userService.bindOrChangeEmail(data);
return Result.judge(result);
}
diff --git a/src/main/java/com/youlai/boot/system/converter/RoleConverter.java b/src/main/java/com/youlai/boot/system/converter/RoleConverter.java
index 2ef57392..ddcfc930 100644
--- a/src/main/java/com/youlai/boot/system/converter/RoleConverter.java
+++ b/src/main/java/com/youlai/boot/system/converter/RoleConverter.java
@@ -26,9 +26,9 @@ public interface RoleConverter {
@Mapping(target = "value", source = "id"),
@Mapping(target = "label", source = "name")
})
- Option entity2Option(Role role);
+ Option toOption(Role role);
- List