mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-12 16:57:12 +08:00
refactor: 优化登录验证码开关代码
This commit is contained in:
@@ -18,9 +18,7 @@ package top.continew.admin.auth.model.req;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.checkerframework.checker.units.qual.N;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@@ -52,20 +50,15 @@ public class AccountLoginReq implements Serializable {
|
|||||||
@NotBlank(message = "密码不能为空")
|
@NotBlank(message = "密码不能为空")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@Schema(description = "是否开启验证码", example = "true")
|
|
||||||
private Boolean unCaptcha;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证码
|
* 验证码
|
||||||
*/
|
*/
|
||||||
@Schema(description = "验证码", example = "ABCD")
|
@Schema(description = "验证码", example = "ABCD")
|
||||||
// @NotBlank(message = "验证码不能为空")
|
|
||||||
private String captcha;
|
private String captcha;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证码标识
|
* 验证码标识
|
||||||
*/
|
*/
|
||||||
@Schema(description = "验证码标识", example = "090b9a2c-1691-4fca-99db-e4ed0cff362f")
|
@Schema(description = "验证码标识", example = "090b9a2c-1691-4fca-99db-e4ed0cff362f")
|
||||||
// @NotBlank(message = "验证码标识不能为空")
|
|
||||||
private String uuid;
|
private String uuid;
|
||||||
}
|
}
|
||||||
|
@@ -54,4 +54,22 @@ public class CaptchaResp implements Serializable {
|
|||||||
*/
|
*/
|
||||||
@Schema(description = "过期时间戳", example = "1714376969409")
|
@Schema(description = "过期时间戳", example = "1714376969409")
|
||||||
private Long expireTime;
|
private Long expireTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否启用", example = "true")
|
||||||
|
private Boolean isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建验证码信息
|
||||||
|
*
|
||||||
|
* @param uuid 验证码标识
|
||||||
|
* @param img 验证码图片(Base64编码,带图片格式:data:image/gif;base64)
|
||||||
|
* @param expireTime 过期时间戳
|
||||||
|
* @return 验证码信息
|
||||||
|
*/
|
||||||
|
public static CaptchaResp of(String uuid, String img, Long expireTime) {
|
||||||
|
return CaptchaResp.builder().uuid(uuid).img(img).expireTime(expireTime).isEnabled(true).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,7 @@ public enum OptionCategoryEnum {
|
|||||||
MAIL,
|
MAIL,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证码配置
|
* 登录配置
|
||||||
*/
|
*/
|
||||||
CAPTCHA,
|
LOGIN,
|
||||||
}
|
}
|
||||||
|
@@ -100,7 +100,7 @@ public class FileDO extends BaseDO {
|
|||||||
.blankToDefault(this.extension, this.name, ex -> this.name + StringConstants.DOT + ex));
|
.blankToDefault(this.extension, this.name, ex -> this.name + StringConstants.DOT + ex));
|
||||||
fileInfo.setBasePath(StringConstants.EMPTY);
|
fileInfo.setBasePath(StringConstants.EMPTY);
|
||||||
// 优化 path 处理
|
// 优化 path 处理
|
||||||
fileInfo.setPath(extractRelativePath(this.url,storageDO));
|
fileInfo.setPath(extractRelativePath(this.url, storageDO));
|
||||||
|
|
||||||
fileInfo.setExt(this.extension);
|
fileInfo.setExt(this.extension);
|
||||||
fileInfo.setPlatform(storageDO.getCode());
|
fileInfo.setPlatform(storageDO.getCode());
|
||||||
@@ -117,6 +117,7 @@ public class FileDO extends BaseDO {
|
|||||||
* 例如:
|
* 例如:
|
||||||
* http://domain.cn/bucketName/2024/11/27/6746ec3b2907f0de80afdd70.png => 2024/11/27/
|
* http://domain.cn/bucketName/2024/11/27/6746ec3b2907f0de80afdd70.png => 2024/11/27/
|
||||||
* http://bucketName.domain.cn/2024/11/27/6746ec3b2907f0de80afdd70.png => 2024/11/27/
|
* http://bucketName.domain.cn/2024/11/27/6746ec3b2907f0de80afdd70.png => 2024/11/27/
|
||||||
|
*
|
||||||
* @param url 文件路径
|
* @param url 文件路径
|
||||||
* @param storageDO 存储桶信息
|
* @param storageDO 存储桶信息
|
||||||
* @return
|
* @return
|
||||||
@@ -124,15 +125,13 @@ public class FileDO extends BaseDO {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private static String extractRelativePath(String url, StorageDO storageDO) {
|
private static String extractRelativePath(String url, StorageDO storageDO) {
|
||||||
url = StrUtil.subBefore(url, StringConstants.SLASH, true) + StringConstants.SLASH;
|
url = StrUtil.subBefore(url, StringConstants.SLASH, true) + StringConstants.SLASH;
|
||||||
if (storageDO.getType().equals(StorageTypeEnum.LOCAL)){
|
if (storageDO.getType().equals(StorageTypeEnum.LOCAL)) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
// 提取 URL 中的路径部分
|
// 提取 URL 中的路径部分
|
||||||
String fullPath = new URL(url).getPath();
|
String fullPath = new URL(url).getPath();
|
||||||
// 移除开头的斜杠
|
// 移除开头的斜杠
|
||||||
String relativePath = fullPath.startsWith(StringConstants.SLASH)
|
String relativePath = fullPath.startsWith(StringConstants.SLASH) ? fullPath.substring(1) : fullPath;
|
||||||
? fullPath.substring(1)
|
|
||||||
: fullPath;
|
|
||||||
// 如果路径以 bucketName 开头,则移除 bucketName 例如: bucketName/2024/11/27/ -> 2024/11/27/
|
// 如果路径以 bucketName 开头,则移除 bucketName 例如: bucketName/2024/11/27/ -> 2024/11/27/
|
||||||
if (relativePath.startsWith(storageDO.getBucketName())) {
|
if (relativePath.startsWith(storageDO.getBucketName())) {
|
||||||
return StrUtil.split(relativePath, storageDO.getBucketName()).get(1);
|
return StrUtil.split(relativePath, storageDO.getBucketName()).get(1);
|
||||||
|
@@ -35,10 +35,12 @@ import top.continew.admin.auth.model.resp.RouteResp;
|
|||||||
import top.continew.admin.auth.model.resp.UserInfoResp;
|
import top.continew.admin.auth.model.resp.UserInfoResp;
|
||||||
import top.continew.admin.auth.service.LoginService;
|
import top.continew.admin.auth.service.LoginService;
|
||||||
import top.continew.admin.common.constant.CacheConstants;
|
import top.continew.admin.common.constant.CacheConstants;
|
||||||
|
import top.continew.admin.common.constant.SysConstants;
|
||||||
import top.continew.admin.common.context.UserContext;
|
import top.continew.admin.common.context.UserContext;
|
||||||
import top.continew.admin.common.context.UserContextHolder;
|
import top.continew.admin.common.context.UserContextHolder;
|
||||||
import top.continew.admin.common.util.SecureUtils;
|
import top.continew.admin.common.util.SecureUtils;
|
||||||
import top.continew.admin.system.model.resp.user.UserDetailResp;
|
import top.continew.admin.system.model.resp.user.UserDetailResp;
|
||||||
|
import top.continew.admin.system.service.OptionService;
|
||||||
import top.continew.admin.system.service.UserService;
|
import top.continew.admin.system.service.UserService;
|
||||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||||
import top.continew.starter.core.util.ExceptionUtils;
|
import top.continew.starter.core.util.ExceptionUtils;
|
||||||
@@ -62,6 +64,7 @@ public class AuthController {
|
|||||||
|
|
||||||
private static final String CAPTCHA_EXPIRED = "验证码已失效";
|
private static final String CAPTCHA_EXPIRED = "验证码已失效";
|
||||||
private static final String CAPTCHA_ERROR = "验证码错误";
|
private static final String CAPTCHA_ERROR = "验证码错误";
|
||||||
|
private final OptionService optionService;
|
||||||
private final LoginService loginService;
|
private final LoginService loginService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
@@ -69,7 +72,11 @@ public class AuthController {
|
|||||||
@Operation(summary = "账号登录", description = "根据账号和密码进行登录认证")
|
@Operation(summary = "账号登录", description = "根据账号和密码进行登录认证")
|
||||||
@PostMapping("/account")
|
@PostMapping("/account")
|
||||||
public LoginResp accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
|
public LoginResp accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
|
||||||
if (!loginReq.getUnCaptcha()) {
|
// 校验验证码
|
||||||
|
int loginCaptchaEnabled = optionService.getValueByCode2Int("LOGIN_CAPTCHA_ENABLED");
|
||||||
|
if (SysConstants.YES.equals(loginCaptchaEnabled)) {
|
||||||
|
ValidationUtils.throwIfBlank(loginReq.getCaptcha(), "验证码不能为空");
|
||||||
|
ValidationUtils.throwIfBlank(loginReq.getUuid(), "验证码标识不能为空");
|
||||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid();
|
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid();
|
||||||
String captcha = RedisUtils.get(captchaKey);
|
String captcha = RedisUtils.get(captchaKey);
|
||||||
ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
|
ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
|
||||||
|
@@ -45,9 +45,10 @@ import org.redisson.api.RateIntervalUnit;
|
|||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import top.continew.admin.auth.model.resp.CaptchaResp;
|
||||||
import top.continew.admin.common.config.properties.CaptchaProperties;
|
import top.continew.admin.common.config.properties.CaptchaProperties;
|
||||||
import top.continew.admin.common.constant.CacheConstants;
|
import top.continew.admin.common.constant.CacheConstants;
|
||||||
import top.continew.admin.auth.model.resp.CaptchaResp;
|
import top.continew.admin.common.constant.SysConstants;
|
||||||
import top.continew.admin.system.enums.OptionCategoryEnum;
|
import top.continew.admin.system.enums.OptionCategoryEnum;
|
||||||
import top.continew.admin.system.service.OptionService;
|
import top.continew.admin.system.service.OptionService;
|
||||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||||
@@ -88,15 +89,6 @@ public class CaptchaController {
|
|||||||
private final GraphicCaptchaService graphicCaptchaService;
|
private final GraphicCaptchaService graphicCaptchaService;
|
||||||
private final OptionService optionService;
|
private final OptionService optionService;
|
||||||
|
|
||||||
|
|
||||||
@Log(ignore = true)
|
|
||||||
@Operation(summary = "获取验证码配置", description = "获取验证码配置(预留后续扩展多种验证码)")
|
|
||||||
@GetMapping("/config")
|
|
||||||
public R getCaptchaConfig() {
|
|
||||||
Map<String, String> captchaConfig = optionService.getByCategory(OptionCategoryEnum.CAPTCHA);
|
|
||||||
return R.ok(captchaConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Log(ignore = true)
|
@Log(ignore = true)
|
||||||
@Operation(summary = "获取行为验证码", description = "获取行为验证码(Base64编码)")
|
@Operation(summary = "获取行为验证码", description = "获取行为验证码(Base64编码)")
|
||||||
@GetMapping("/behavior")
|
@GetMapping("/behavior")
|
||||||
@@ -120,13 +112,17 @@ public class CaptchaController {
|
|||||||
@Operation(summary = "获取图片验证码", description = "获取图片验证码(Base64编码,带图片格式:data:image/gif;base64)")
|
@Operation(summary = "获取图片验证码", description = "获取图片验证码(Base64编码,带图片格式:data:image/gif;base64)")
|
||||||
@GetMapping("/image")
|
@GetMapping("/image")
|
||||||
public CaptchaResp getImageCaptcha() {
|
public CaptchaResp getImageCaptcha() {
|
||||||
|
int loginCaptchaEnabled = optionService.getValueByCode2Int("LOGIN_CAPTCHA_ENABLED");
|
||||||
|
if (SysConstants.NO.equals(loginCaptchaEnabled)) {
|
||||||
|
return CaptchaResp.builder().isEnabled(false).build();
|
||||||
|
}
|
||||||
String uuid = IdUtil.fastUUID();
|
String uuid = IdUtil.fastUUID();
|
||||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid;
|
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid;
|
||||||
Captcha captcha = graphicCaptchaService.getCaptcha();
|
Captcha captcha = graphicCaptchaService.getCaptcha();
|
||||||
long expireTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now()
|
long expireTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now()
|
||||||
.plusMinutes(captchaProperties.getExpirationInMinutes()));
|
.plusMinutes(captchaProperties.getExpirationInMinutes()));
|
||||||
RedisUtils.set(captchaKey, captcha.text(), Duration.ofMinutes(captchaProperties.getExpirationInMinutes()));
|
RedisUtils.set(captchaKey, captcha.text(), Duration.ofMinutes(captchaProperties.getExpirationInMinutes()));
|
||||||
return CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build();
|
return CaptchaResp.of(uuid, captcha.toBase64(), expireTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +209,7 @@ public class CaptchaController {
|
|||||||
messageMap.put("captcha", captcha);
|
messageMap.put("captcha", captcha);
|
||||||
messageMap.put("expirationInMinutes", String.valueOf(expirationInMinutes));
|
messageMap.put("expirationInMinutes", String.valueOf(expirationInMinutes));
|
||||||
SmsResponse smsResponse = smsBlend.sendMessage(phone, captchaSms
|
SmsResponse smsResponse = smsBlend.sendMessage(phone, captchaSms
|
||||||
.getTemplateId(), (LinkedHashMap<String, String>) messageMap);
|
.getTemplateId(), (LinkedHashMap<String, String>)messageMap);
|
||||||
CheckUtils.throwIf(!smsResponse.isSuccess(), "验证码发送失败");
|
CheckUtils.throwIf(!smsResponse.isSuccess(), "验证码发送失败");
|
||||||
// 保存验证码
|
// 保存验证码
|
||||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
|
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
|
||||||
|
@@ -142,9 +142,8 @@ VALUES
|
|||||||
(18, 'MAIL', '用户名', 'MAIL_USERNAME', NULL, 'charles7c@126.com', NULL, NULL, NULL),
|
(18, 'MAIL', '用户名', 'MAIL_USERNAME', NULL, 'charles7c@126.com', NULL, NULL, NULL),
|
||||||
(19, 'MAIL', '密码', 'MAIL_PASSWORD', NULL, NULL, NULL, NULL, NULL),
|
(19, 'MAIL', '密码', 'MAIL_PASSWORD', NULL, NULL, NULL, NULL, NULL),
|
||||||
(20, 'MAIL', '是否启用SSL', 'MAIL_SSL_ENABLED', NULL, '1', NULL, NULL, NULL),
|
(20, 'MAIL', '是否启用SSL', 'MAIL_SSL_ENABLED', NULL, '1', NULL, NULL, NULL),
|
||||||
(21, 'MAIL', 'SSL端口', 'MAIL_SSL_PORT', NULL, '465', NULL, NULL, NULL);
|
(21, 'MAIL', 'SSL端口', 'MAIL_SSL_PORT', NULL, '465', NULL, NULL, NULL),
|
||||||
(22, 'CAPTCHA', '是否开启验证码', 'NEED_CAPTCHA', '1', '1', '是否开启验证码(1:开启 0:关闭)', 1, '2024-12-04 16:19:58');
|
(22, 'LOGIN', '是否启用验证码', 'LOGIN_CAPTCHA_ENABLED', NULL, '1', '是否启用验证码(1:是;0:否)', NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
-- 初始化默认字典
|
-- 初始化默认字典
|
||||||
INSERT INTO `sys_dict`
|
INSERT INTO `sys_dict`
|
||||||
|
@@ -142,8 +142,8 @@ VALUES
|
|||||||
(18, 'MAIL', '用户名', 'MAIL_USERNAME', NULL, 'charles7c@126.com', NULL, NULL, NULL),
|
(18, 'MAIL', '用户名', 'MAIL_USERNAME', NULL, 'charles7c@126.com', NULL, NULL, NULL),
|
||||||
(19, 'MAIL', '密码', 'MAIL_PASSWORD', NULL, NULL, NULL, NULL, NULL),
|
(19, 'MAIL', '密码', 'MAIL_PASSWORD', NULL, NULL, NULL, NULL, NULL),
|
||||||
(20, 'MAIL', '是否启用SSL', 'MAIL_SSL_ENABLED', NULL, '1', NULL, NULL, NULL),
|
(20, 'MAIL', '是否启用SSL', 'MAIL_SSL_ENABLED', NULL, '1', NULL, NULL, NULL),
|
||||||
(21, 'MAIL', 'SSL端口', 'MAIL_SSL_PORT', NULL, '465', NULL, NULL, NULL);
|
(21, 'MAIL', 'SSL端口', 'MAIL_SSL_PORT', NULL, '465', NULL, NULL, NULL),
|
||||||
(22, 'CAPTCHA', '是否开启验证码', 'NEED_CAPTCHA', '1', '1', '是否开启验证码(1:开启 0:关闭)', 1, '2024-12-04 16:19:58');
|
(22, 'LOGIN', '是否启用验证码', 'LOGIN_CAPTCHA_ENABLED', NULL, '1', '是否启用验证码(1:是;0:否)', NULL, NULL);
|
||||||
|
|
||||||
-- 初始化默认字典
|
-- 初始化默认字典
|
||||||
INSERT INTO "sys_dict"
|
INSERT INTO "sys_dict"
|
||||||
|
Reference in New Issue
Block a user