mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-22 20:54:26 +08:00
refactor: 验证码重构
This commit is contained in:
parent
6620b5434d
commit
fe80aa3b76
@ -76,21 +76,21 @@ public class CaptchaAuthenticationConverter implements AuthenticationConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证码(必需)
|
// 验证码(必需)
|
||||||
String verifyCode = parameters.getFirst(CaptchaParameterNames.VERIFY_CODE);
|
String verifyCode = parameters.getFirst(CaptchaParameterNames.CODE);
|
||||||
if (StrUtil.isBlank(verifyCode)) {
|
if (StrUtil.isBlank(verifyCode)) {
|
||||||
OAuth2EndpointUtils.throwError(
|
OAuth2EndpointUtils.throwError(
|
||||||
OAuth2ErrorCodes.INVALID_REQUEST,
|
OAuth2ErrorCodes.INVALID_REQUEST,
|
||||||
CaptchaParameterNames.VERIFY_CODE,
|
CaptchaParameterNames.CODE,
|
||||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI
|
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证码缓存Key(必需)
|
// 验证码缓存Key(必需)
|
||||||
String verifyCodeKey = parameters.getFirst(CaptchaParameterNames.VERIFY_CODE_KEY);
|
String verifyKey = parameters.getFirst(CaptchaParameterNames.KEY);
|
||||||
if (StrUtil.isBlank(verifyCodeKey)) {
|
if (StrUtil.isBlank(verifyKey)) {
|
||||||
OAuth2EndpointUtils.throwError(
|
OAuth2EndpointUtils.throwError(
|
||||||
OAuth2ErrorCodes.INVALID_REQUEST,
|
OAuth2ErrorCodes.INVALID_REQUEST,
|
||||||
CaptchaParameterNames.VERIFY_CODE_KEY,
|
CaptchaParameterNames.KEY,
|
||||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI
|
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package com.youlai.auth.authentication.captcha;
|
|||||||
|
|
||||||
import cn.hutool.captcha.generator.MathGenerator;
|
import cn.hutool.captcha.generator.MathGenerator;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.youlai.auth.util.OAuth2AuthenticationProviderUtils;
|
import com.youlai.auth.util.OAuth2AuthenticationProviderUtils;
|
||||||
import com.youlai.common.constant.SecurityConstants;
|
import com.youlai.common.constant.SecurityConstants;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -15,6 +13,10 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.oauth2.core.*;
|
import org.springframework.security.oauth2.core.*;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
||||||
@ -25,10 +27,14 @@ import org.springframework.security.oauth2.server.authorization.context.Authoriz
|
|||||||
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
|
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
|
||||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
|
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
|
||||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证码模式身份验证提供者
|
* 验证码模式身份验证提供者
|
||||||
@ -43,6 +49,7 @@ import java.util.Map;
|
|||||||
public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||||
|
private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final OAuth2AuthorizationService authorizationService;
|
private final OAuth2AuthorizationService authorizationService;
|
||||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||||
@ -84,10 +91,10 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
|||||||
|
|
||||||
// 验证码校验
|
// 验证码校验
|
||||||
Map<String, Object> additionalParameters = captchaAuthenticationToken.getAdditionalParameters();
|
Map<String, Object> additionalParameters = captchaAuthenticationToken.getAdditionalParameters();
|
||||||
String verifyCode = (String) additionalParameters.get(CaptchaParameterNames.VERIFY_CODE);
|
String verifyCode = (String) additionalParameters.get(CaptchaParameterNames.CODE);
|
||||||
String verifyCodeKey = (String) additionalParameters.get(CaptchaParameterNames.VERIFY_CODE_KEY);
|
String verifyCodeKey = (String) additionalParameters.get(CaptchaParameterNames.KEY);
|
||||||
|
|
||||||
String cacheCode = redisTemplate.opsForValue().get(SecurityConstants.VERIFY_CODE_CACHE_KEY_PREFIX + verifyCodeKey);
|
String cacheCode = redisTemplate.opsForValue().get(SecurityConstants.CAPTCHA_CODE_PREFIX + verifyCodeKey);
|
||||||
|
|
||||||
// 验证码比对
|
// 验证码比对
|
||||||
MathGenerator mathGenerator = new MathGenerator();
|
MathGenerator mathGenerator = new MathGenerator();
|
||||||
@ -109,6 +116,20 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
|||||||
throw new OAuth2AuthenticationException(e.getCause() != null ? e.getCause().getMessage() : e.getMessage());
|
throw new OAuth2AuthenticationException(e.getCause() != null ? e.getCause().getMessage() : e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证申请访问范围(Scope)
|
||||||
|
Set<String> authorizedScopes = registeredClient.getScopes();
|
||||||
|
Set<String> requestedScopes = captchaAuthenticationToken.getScopes();
|
||||||
|
if (!CollectionUtils.isEmpty(requestedScopes)) {
|
||||||
|
Set<String> unauthorizedScopes = requestedScopes.stream()
|
||||||
|
.filter(requestedScope -> !registeredClient.getScopes().contains(requestedScope))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (!CollectionUtils.isEmpty(unauthorizedScopes)) {
|
||||||
|
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
|
||||||
|
}
|
||||||
|
authorizedScopes = new LinkedHashSet<>(requestedScopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 访问令牌(Access Token) 构造器
|
// 访问令牌(Access Token) 构造器
|
||||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||||
.registeredClient(registeredClient)
|
.registeredClient(registeredClient)
|
||||||
@ -131,10 +152,6 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
|||||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||||
|
|
||||||
|
|
||||||
// 权限数据比较多通过反射移除不持久化至数据库
|
|
||||||
ReflectUtil.setFieldValue(usernamePasswordAuthentication.getPrincipal(), "perms", null);
|
|
||||||
|
|
||||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||||
.principalName(usernamePasswordAuthentication.getName())
|
.principalName(usernamePasswordAuthentication.getName())
|
||||||
.authorizationGrantType(CaptchaAuthenticationToken.CAPTCHA)
|
.authorizationGrantType(CaptchaAuthenticationToken.CAPTCHA)
|
||||||
@ -164,10 +181,42 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
|||||||
authorizationBuilder.refreshToken(refreshToken);
|
authorizationBuilder.refreshToken(refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
// ----- ID token -----
|
||||||
|
OidcIdToken idToken;
|
||||||
|
if (requestedScopes.contains(OidcScopes.OPENID)) {
|
||||||
|
// @formatter:off
|
||||||
|
tokenContext = tokenContextBuilder
|
||||||
|
.tokenType(ID_TOKEN_TOKEN_TYPE)
|
||||||
|
.authorization(authorizationBuilder.build()) // ID token customizer may need access to the access token and/or refresh token
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
OAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext);
|
||||||
|
if (!(generatedIdToken instanceof Jwt)) {
|
||||||
|
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||||
|
"The token generator failed to generate the ID token.", ERROR_URI);
|
||||||
|
throw new OAuth2AuthenticationException(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("Generated id token");
|
||||||
|
}
|
||||||
|
|
||||||
|
idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),
|
||||||
|
generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());
|
||||||
|
authorizationBuilder.token(idToken, (metadata) ->
|
||||||
|
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
|
||||||
|
} else {
|
||||||
|
idToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 持久化令牌发放记录到数据库
|
// 持久化令牌发放记录到数据库
|
||||||
|
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||||
this.authorizationService.save(authorization);
|
this.authorizationService.save(authorization);
|
||||||
additionalParameters = Collections.EMPTY_MAP;
|
|
||||||
|
additionalParameters = (idToken != null)
|
||||||
|
? Collections.singletonMap(OidcParameterNames.ID_TOKEN, idToken.getTokenValue())
|
||||||
|
: Collections.emptyMap();
|
||||||
|
|
||||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
|
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,15 +9,19 @@ package com.youlai.auth.authentication.captcha;
|
|||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
public final class CaptchaParameterNames {
|
public final class CaptchaParameterNames {
|
||||||
/**
|
|
||||||
* 验证码
|
|
||||||
*/
|
|
||||||
public static final String VERIFY_CODE = "verifyCode";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证码缓存Key
|
* 验证码缓存Key (唯一标识) 用于从Redis获取验证码Code
|
||||||
*/
|
*/
|
||||||
public static final String VERIFY_CODE_KEY = "verifyCodeKey";
|
public static final String KEY = "captchaKey";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码 Code
|
||||||
|
*/
|
||||||
|
public static final String CODE = "captchaCode";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private CaptchaParameterNames() {
|
private CaptchaParameterNames() {
|
||||||
|
@ -35,8 +35,8 @@ public class CaptchaAuthenticationTests {
|
|||||||
.param(OAuth2ParameterNames.GRANT_TYPE, "captcha")
|
.param(OAuth2ParameterNames.GRANT_TYPE, "captcha")
|
||||||
.param(OAuth2ParameterNames.USERNAME, "admin")
|
.param(OAuth2ParameterNames.USERNAME, "admin")
|
||||||
.param(OAuth2ParameterNames.PASSWORD, "123456")
|
.param(OAuth2ParameterNames.PASSWORD, "123456")
|
||||||
.param(CaptchaParameterNames.VERIFY_CODE, "123456")
|
.param(CaptchaParameterNames.CODE, "123456")
|
||||||
.param(CaptchaParameterNames.VERIFY_CODE_KEY, "123456")
|
.param(CaptchaParameterNames.KEY, "123456")
|
||||||
.headers(headers))
|
.headers(headers))
|
||||||
.andDo(print())
|
.andDo(print())
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.youlai.gateway.config;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.captcha.generator.MathGenerator;
|
||||||
|
import cn.hutool.captcha.generator.RandomGenerator;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码自动装配配置
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/24
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CaptchaConfig {
|
||||||
|
|
||||||
|
private final CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码文字生成器
|
||||||
|
*
|
||||||
|
* @return CodeGenerator
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CodeGenerator codeGenerator() {
|
||||||
|
String codeType = captchaProperties.getCode().getType();
|
||||||
|
int codeLength = captchaProperties.getCode().getLength();
|
||||||
|
if ("math".equalsIgnoreCase(codeType)) {
|
||||||
|
return new MathGenerator(codeLength);
|
||||||
|
} else if ("random".equalsIgnoreCase(codeType)) {
|
||||||
|
return new RandomGenerator(codeLength);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid captcha generator type: " + codeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字体
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Font captchaFont() {
|
||||||
|
String fontName = captchaProperties.getFont().getName();
|
||||||
|
int fontSize = captchaProperties.getFont().getSize();
|
||||||
|
int fontWight = captchaProperties.getFont().getWeight();
|
||||||
|
return new Font(fontName, fontWight, fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package com.youlai.gateway.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码配置
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/24
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "captcha")
|
||||||
|
@Data
|
||||||
|
public class CaptchaProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类型 circle-圆圈干扰验证码|gif-Gif验证码|line-干扰线验证码|shear-扭曲干扰验证码
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码图片宽度
|
||||||
|
*/
|
||||||
|
private int width;
|
||||||
|
/**
|
||||||
|
* 验证码图片高度
|
||||||
|
*/
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 干扰线数量
|
||||||
|
*/
|
||||||
|
private int interfereCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本透明度
|
||||||
|
*/
|
||||||
|
private Float textAlpha;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码过期时间,单位:秒
|
||||||
|
*/
|
||||||
|
private Long expireSeconds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字符配置
|
||||||
|
*/
|
||||||
|
private CodeProperties code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字体
|
||||||
|
*/
|
||||||
|
private FontProperties font;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字符配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class CodeProperties {
|
||||||
|
/**
|
||||||
|
* 验证码字符类型 math-算术|random-随机字符串
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
/**
|
||||||
|
* 验证码字符长度,type=算术时,表示运算位数(1:个位数 2:十位数);type=随机字符时,表示字符个数
|
||||||
|
*/
|
||||||
|
private int length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字体配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class FontProperties {
|
||||||
|
/**
|
||||||
|
* 字体名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 字体样式 0-普通|1-粗体|2-斜体
|
||||||
|
*/
|
||||||
|
private int weight;
|
||||||
|
/**
|
||||||
|
* 字体大小
|
||||||
|
*/
|
||||||
|
private int size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.youlai.gateway.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EasyCaptcha 验证码类型枚举
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
public enum CaptchaTypeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 圆圈干扰验证码
|
||||||
|
*/
|
||||||
|
CIRCLE,
|
||||||
|
/**
|
||||||
|
* GIF验证码
|
||||||
|
*/
|
||||||
|
GIF,
|
||||||
|
/**
|
||||||
|
* 干扰线验证码
|
||||||
|
*/
|
||||||
|
LINE,
|
||||||
|
/**
|
||||||
|
* 扭曲干扰验证码
|
||||||
|
*/
|
||||||
|
SHEAR
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
package com.youlai.gateway.handler;
|
package com.youlai.gateway.handler;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
import cn.hutool.captcha.CaptchaUtil;
|
import cn.hutool.captcha.CaptchaUtil;
|
||||||
import cn.hutool.captcha.CircleCaptcha;
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
import cn.hutool.captcha.GifCaptcha;
|
|
||||||
import cn.hutool.captcha.generator.MathGenerator;
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import com.youlai.common.constant.SecurityConstants;
|
import com.youlai.common.constant.SecurityConstants;
|
||||||
import com.youlai.common.result.Result;
|
import com.youlai.common.result.Result;
|
||||||
|
import com.youlai.gateway.config.CaptchaProperties;
|
||||||
|
import com.youlai.gateway.enums.CaptchaTypeEnum;
|
||||||
|
import com.youlai.gateway.model.CaptchaResult;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -16,8 +18,7 @@ import org.springframework.web.reactive.function.server.ServerRequest;
|
|||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.awt.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,25 +32,48 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class CaptchaHandler implements HandlerFunction<ServerResponse> {
|
public class CaptchaHandler implements HandlerFunction<ServerResponse> {
|
||||||
|
|
||||||
private final StringRedisTemplate redisTemplate;
|
private final StringRedisTemplate redisTemplate;
|
||||||
|
private final CodeGenerator codeGenerator;
|
||||||
|
private final Font captchaFont;
|
||||||
|
private final CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||||
|
|
||||||
MathGenerator mathGenerator = new MathGenerator(1);
|
String captchaType = captchaProperties.getType();
|
||||||
CircleCaptcha circleCaptcha = new CircleCaptcha(150, 25, 4, 3);
|
int width = captchaProperties.getWidth();
|
||||||
circleCaptcha.setGenerator(mathGenerator);
|
int height = captchaProperties.getHeight();
|
||||||
String captchaCode = circleCaptcha.getCode(); // 验证码
|
int interfereCount = captchaProperties.getInterfereCount();
|
||||||
String captchaBase64 = circleCaptcha.getImageBase64Data(); // 验证码图片Base64
|
int codeLength = captchaProperties.getCode().getLength();
|
||||||
|
|
||||||
|
AbstractCaptcha captcha;
|
||||||
|
if (CaptchaTypeEnum.CIRCLE.name().equalsIgnoreCase(captchaType)) {
|
||||||
|
captcha = CaptchaUtil.createCircleCaptcha(width, height, codeLength, interfereCount);
|
||||||
|
} else if (CaptchaTypeEnum.GIF.name().equalsIgnoreCase(captchaType)) {
|
||||||
|
captcha = CaptchaUtil.createGifCaptcha(width, height, codeLength);
|
||||||
|
} else if (CaptchaTypeEnum.LINE.name().equalsIgnoreCase(captchaType)) {
|
||||||
|
captcha = CaptchaUtil.createLineCaptcha(width, height, codeLength, interfereCount);
|
||||||
|
} else if (CaptchaTypeEnum.SHEAR.name().equalsIgnoreCase(captchaType)) {
|
||||||
|
captcha = CaptchaUtil.createShearCaptcha(width, height, codeLength, interfereCount);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid captcha type: " + captchaType);
|
||||||
|
}
|
||||||
|
captcha.setGenerator(codeGenerator);
|
||||||
|
captcha.setTextAlpha(captchaProperties.getTextAlpha());
|
||||||
|
captcha.setFont(captchaFont);
|
||||||
|
|
||||||
|
String captchaCode = captcha.getCode();
|
||||||
|
String imageBase64Data = captcha.getImageBase64Data();
|
||||||
|
|
||||||
// 验证码文本缓存至Redis,用于登录校验
|
// 验证码文本缓存至Redis,用于登录校验
|
||||||
String verifyCodeKey = IdUtil.fastSimpleUUID();
|
String captchaId = IdUtil.fastSimpleUUID();
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.VERIFY_CODE_CACHE_KEY_PREFIX + verifyCodeKey, captchaCode,
|
redisTemplate.opsForValue().set(SecurityConstants.CAPTCHA_CODE_PREFIX + captchaId, captchaCode,
|
||||||
120, TimeUnit.SECONDS);
|
captchaProperties.getExpireSeconds(), TimeUnit.SECONDS);
|
||||||
|
|
||||||
Map<String, String> result = new HashMap<>(2);
|
CaptchaResult captchaResult = CaptchaResult.builder()
|
||||||
result.put("verifyCodeKey", verifyCodeKey);
|
.captchaId(captchaId)
|
||||||
result.put("captchaImgBase64", captchaBase64);
|
.captchaBase64(imageBase64Data)
|
||||||
|
.build();
|
||||||
|
|
||||||
return ServerResponse.ok().body(BodyInserters.fromValue(Result.success(result)));
|
return ServerResponse.ok().body(BodyInserters.fromValue(Result.success(captchaResult)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.youlai.gateway.model;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码响应对象
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/03/24
|
||||||
|
*/
|
||||||
|
@Schema(description ="验证码响应对象")
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CaptchaResult {
|
||||||
|
|
||||||
|
@Schema(description = "验证码唯一标识(用于从Redis获取验证码Code)")
|
||||||
|
private String captchaId;
|
||||||
|
|
||||||
|
@Schema(description = "验证码图片Base64字符串")
|
||||||
|
private String captchaBase64;
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user