Merge branch 'master' of gitee.com:youlaiorg/youlai-boot
This commit is contained in:
BIN
docs/images/qr/wechat-app.jpg
Normal file
BIN
docs/images/qr/wechat-app.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>youlai-boot</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<version>4.3.1</version>
|
||||
<description>基于 Java 17 + SpringBoot 4 + Spring Security 构建的权限管理系统。</description>
|
||||
|
||||
<parent>
|
||||
|
||||
@@ -3,13 +3,13 @@ package com.youlai.boot.framework.security.exception;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* 验证码校验异常
|
||||
* 短信验证码异常
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2025/3/1
|
||||
*/
|
||||
public class CaptchaValidationException extends AuthenticationException {
|
||||
public CaptchaValidationException(String msg) {
|
||||
public class SmsCaptchaException extends AuthenticationException {
|
||||
public SmsCaptchaException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.youlai.boot.framework.security.exception;
|
||||
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Token 无效异常(access_token 或 refresh_token 过期/无效)
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 4.3.1
|
||||
*/
|
||||
@Getter
|
||||
public class TokenInvalidException extends RuntimeException {
|
||||
|
||||
private final ResultCode resultCode;
|
||||
|
||||
public TokenInvalidException(ResultCode resultCode) {
|
||||
super(resultCode.getMsg());
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package com.youlai.boot.framework.security.provider;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.framework.security.exception.CaptchaValidationException;
|
||||
import com.youlai.boot.framework.security.exception.SmsCaptchaException;
|
||||
import com.youlai.boot.framework.security.model.SmsAuthenticationToken;
|
||||
import com.youlai.boot.framework.security.model.SysUserDetails;
|
||||
import com.youlai.boot.framework.security.model.UserAuthInfo;
|
||||
@@ -62,11 +62,11 @@ public class SmsAuthenticationProvider implements AuthenticationProvider {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(mobile)) {
|
||||
log.warn("短信验证码登录失败:手机号为空");
|
||||
throw new CaptchaValidationException("手机号不能为空");
|
||||
throw new SmsCaptchaException("手机号不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(inputVerifyCode)) {
|
||||
log.warn("短信验证码登录失败:验证码为空,手机号={}", mobile);
|
||||
throw new CaptchaValidationException("验证码不能为空");
|
||||
throw new SmsCaptchaException("验证码不能为空");
|
||||
}
|
||||
|
||||
// 根据手机号获取用户信息
|
||||
@@ -89,12 +89,12 @@ public class SmsAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
if (cachedVerifyCode == null) {
|
||||
log.warn("短信验证码登录失败:验证码已过期,手机号={}", mobile);
|
||||
throw new CaptchaValidationException("验证码已过期,请重新获取");
|
||||
throw new SmsCaptchaException("验证码已过期,请重新获取");
|
||||
}
|
||||
|
||||
if (!StrUtil.equals(inputVerifyCode, cachedVerifyCode)) {
|
||||
log.warn("短信验证码登录失败:验证码错误,手机号={}", mobile);
|
||||
throw new CaptchaValidationException("验证码错误");
|
||||
throw new SmsCaptchaException("验证码错误");
|
||||
}
|
||||
|
||||
// 验证成功后删除验证码,防止重复使用
|
||||
|
||||
@@ -12,9 +12,9 @@ import cn.hutool.jwt.JWTUtil;
|
||||
import com.youlai.boot.common.constant.JwtClaimConstants;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.common.constant.SecurityConstants;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.framework.security.config.SecurityProperties;
|
||||
import com.youlai.boot.framework.security.exception.TokenInvalidException;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.framework.security.model.RoleDataScope;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -300,7 +300,7 @@ public class JwtTokenManager implements TokenManager {
|
||||
public AuthenticationToken refreshToken(String refreshToken) {
|
||||
boolean isValid = validateRefreshToken(refreshToken);
|
||||
if (!isValid) {
|
||||
throw new BusinessException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||
throw new TokenInvalidException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||
}
|
||||
Authentication authentication = parseToken(refreshToken);
|
||||
int accessTokenExpiration = securityProperties.getSession().getAccessTokenTimeToLive();
|
||||
|
||||
@@ -5,9 +5,9 @@ import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.common.constant.SecurityConstants;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.framework.security.config.SecurityProperties;
|
||||
import com.youlai.boot.framework.security.exception.TokenInvalidException;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.framework.security.model.UserSession;
|
||||
import com.youlai.boot.framework.security.model.SysUserDetails;
|
||||
@@ -147,7 +147,7 @@ public class RedisTokenManager implements TokenManager {
|
||||
UserSession userSession = (UserSession) redisTemplate.opsForValue()
|
||||
.get(StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken));
|
||||
if (userSession == null) {
|
||||
throw new BusinessException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||
throw new TokenInvalidException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||
}
|
||||
Object oldAccessTokenValue = redisTemplate.opsForValue().get(StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userSession.getUserId()));
|
||||
// 删除旧的访问令牌记录
|
||||
|
||||
@@ -5,6 +5,7 @@ import tools.jackson.core.JacksonException;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.framework.security.exception.TokenInvalidException;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
@@ -222,6 +223,17 @@ public class GlobalExceptionHandler {
|
||||
return Result.failed(ResultCode.INTEGRITY_CONSTRAINT_VIOLATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Token 无效异常
|
||||
* <p>
|
||||
* 当 access_token 或 refresh_token 过期/无效时,返回 401。
|
||||
*/
|
||||
@ExceptionHandler(TokenInvalidException.class)
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public <T> Result<T> handleTokenInvalidException(TokenInvalidException e) {
|
||||
return Result.failed(e.getResultCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理业务异常
|
||||
* <p>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#else
|
||||
<if test="queryParams.${fieldConfig.fieldName} != null">
|
||||
#end
|
||||
#set ($queryType = ${fieldConfig.queryType}.name())
|
||||
#set ($queryType = $fieldConfig.queryType.name())
|
||||
#if($queryType == "EQ")
|
||||
AND ${fieldConfig.columnName} = #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "LIKE")
|
||||
|
||||
Reference in New Issue
Block a user