This commit is contained in:
Ray.Hao
2025-03-03 15:41:03 +08:00
8 changed files with 52 additions and 22 deletions

View File

@@ -40,7 +40,7 @@ services:
image: minio/minio:latest image: minio/minio:latest
container_name: minio container_name: minio
restart: unless-stopped restart: unless-stopped
command: server /data --console-address ":9090" command: server /data --console-address ":9001"
ports: ports:
- 9000:9000 - 9000:9000
- 9001:9001 - 9001:9001

View File

@@ -4,13 +4,13 @@
## 安装 ## 安装
```bash ```bash
docker-compose -f docker-compose.yml -p youlai-boot up -d docker-compose -f ./docker-compose.yml -p youlai-boot up -d
``` ```
- p youlai-boot 指定命名空间,避免与其他容器冲突,这里方便管理,统一管理和卸载 - p youlai-boot 指定命名空间,避免与其他容器冲突,这里方便管理,统一管理和卸载
## 卸载 ## 卸载
```bash ```bash
docker-compose -f docker-compose.yml -p youlai-boot down docker-compose -f ./docker-compose.yml -p youlai-boot down
``` ```

View File

@@ -67,7 +67,7 @@ public enum ResultCode implements IResultCode, Serializable {
REFRESH_TOKEN_INVALID("A0231", "刷新令牌无效或已过期"), REFRESH_TOKEN_INVALID("A0231", "刷新令牌无效或已过期"),
// 验证码错误 // 验证码错误
USER_VERIFICATION_CODE_ERROR("A0240", "用户验证码错误"), USER_VERIFICATION_CODE_ERROR("A0240", "验证码错误"),
USER_VERIFICATION_CODE_ATTEMPT_LIMIT_EXCEEDED("A0241", "用户验证码尝试次数超限"), USER_VERIFICATION_CODE_ATTEMPT_LIMIT_EXCEEDED("A0241", "用户验证码尝试次数超限"),
USER_VERIFICATION_CODE_EXPIRED("A0242", "用户验证码过期"), USER_VERIFICATION_CODE_EXPIRED("A0242", "用户验证码过期"),

View File

@@ -0,0 +1,15 @@
package com.youlai.boot.core.security.exception;
import org.springframework.security.core.AuthenticationException;
/**
* 验证码校验异常
*
* @author Ray.Hao
* @since 2025/3/1
*/
public class CaptchaValidationException extends AuthenticationException {
public CaptchaValidationException(String msg) {
super(msg);
}
}

View File

@@ -2,36 +2,47 @@ package com.youlai.boot.core.security.exception;
import com.youlai.boot.common.result.ResultCode; import com.youlai.boot.common.result.ResultCode;
import com.youlai.boot.common.util.ResponseUtils; import com.youlai.boot.common.util.ResponseUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException; import java.io.IOException;
/** /**
* 未认证异常处理器 * 统一处理 Spring Security 认证失败响应
* *
* @author Ray.Hao * @author Ray.Hao
* @since 2.0.0 * @since 2.0.0
*/ */
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint { public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* 认证失败处理入口方法
*
* @param request 触发异常的请求对象(可用于获取请求头、参数等)
* @param response 响应对象(用于写入错误信息)
* @param authException 认证异常对象(包含具体失败原因)
*/
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
int status = response.getStatus(); if (authException instanceof BadCredentialsException) {
if (status == HttpServletResponse.SC_NOT_FOUND) { // 用户名或密码错误
// 资源不存在 ResponseUtils.writeErrMsg(response, ResultCode.USER_PASSWORD_ERROR);
ResponseUtils.writeErrMsg(response, ResultCode.USER_RESOURCE_NOT_FOUND); } else if(authException instanceof InsufficientAuthenticationException){
// 请求头缺失Authorization、Token格式错误、Token过期、签名验证失败
ResponseUtils.writeErrMsg(response, ResultCode.ACCESS_TOKEN_INVALID);
} else { } else {
if (authException instanceof BadCredentialsException) { // 其他未明确处理的认证异常(如账户被锁定、账户禁用等)
// 用户名或密码错误 ResponseUtils.writeErrMsg(response, ResultCode.USER_LOGIN_EXCEPTION, authException.getMessage());
ResponseUtils.writeErrMsg(response, ResultCode.USER_PASSWORD_ERROR, authException.getMessage());
} else {
// 登录异常
ResponseUtils.writeErrMsg(response, ResultCode.USER_LOGIN_EXCEPTION, authException.getMessage());
}
} }
} }
} }

View File

@@ -3,13 +3,13 @@ package com.youlai.boot.core.security.extension.sms;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.RedisConstants;
import com.youlai.boot.core.security.exception.CaptchaValidationException;
import com.youlai.boot.core.security.model.SysUserDetails; import com.youlai.boot.core.security.model.SysUserDetails;
import com.youlai.boot.system.model.dto.UserAuthInfo; import com.youlai.boot.system.model.dto.UserAuthInfo;
import com.youlai.boot.system.service.UserService; import com.youlai.boot.system.service.UserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
@@ -64,7 +64,7 @@ public class SmsAuthenticationProvider implements AuthenticationProvider {
String cachedVerifyCode = (String) redisTemplate.opsForValue().get(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile); String cachedVerifyCode = (String) redisTemplate.opsForValue().get(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile);
if (!StrUtil.equals(inputVerifyCode, cachedVerifyCode)) { if (!StrUtil.equals(inputVerifyCode, cachedVerifyCode)) {
throw new BadCredentialsException("验证码错误"); throw new CaptchaValidationException("验证码错误");
} else { } else {
// 验证成功后删除验证码 // 验证成功后删除验证码
redisTemplate.delete(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile); redisTemplate.delete(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile);

View File

@@ -21,7 +21,7 @@ import java.util.List;
/** /**
* 日志控制层 * 日志控制层
* *
* @author Ray * @author Ray.Hao
* @since 2.10.0 * @since 2.10.0
*/ */
@Tag(name = "13.日志接口") @Tag(name = "13.日志接口")

View File

@@ -7,6 +7,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
#if(${hasLocalDateTime}) #if(${hasLocalDateTime})
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
#end #end
#if(${hasBigDecimal}) #if(${hasBigDecimal})
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -46,6 +47,9 @@ public class ${entityName}Form implements Serializable {
#if($fieldConfig.maxLength) #if($fieldConfig.maxLength)
@Size(max=$fieldConfig.maxLength, message="$fieldConfig.fieldComment长度不能超过${fieldConfig.maxLength}个字符") @Size(max=$fieldConfig.maxLength, message="$fieldConfig.fieldComment长度不能超过${fieldConfig.maxLength}个字符")
#end #end
#if($fieldConfig.fieldType == 'LocalDateTime')
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
#end
private ${fieldConfig.fieldType} ${fieldConfig.fieldName}; private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
#end #end