mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7b9fd923b | |||
| a7fca6ee57 | |||
| cfab8ce3ac |
@@ -12,11 +12,10 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>tianai-captcha</module>
|
<module>tianai-captcha</module>
|
||||||
<module>tianai-captcha-springboot-starter</module>
|
<module>tianai-captcha-springboot-starter</module>
|
||||||
<module>tianai-captcha-solon-plugin</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.5.4</revision>
|
<revision>1.5.5</revision>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<!-- 打包跳过单元测试 -->
|
<!-- 打包跳过单元测试 -->
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cloud.tianai.captcha</groupId>
|
<groupId>cloud.tianai.captcha</groupId>
|
||||||
<artifactId>tianai-captcha-springboot-starter</artifactId>
|
<artifactId>tianai-captcha-springboot-starter</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>cloud.tianai.captcha</groupId>
|
|
||||||
<artifactId>tianai-captcha-parent</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>tianai-captcha-solon-plugin</artifactId>
|
|
||||||
<name>tianai-captcha-solon-plugin</name>
|
|
||||||
<description>行为验证码的solon插件</description>
|
|
||||||
<url>https://gitee.com/tianai/tianai-captcha-solon-plugin</url>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<solon.version>3.5.2</solon.version>
|
|
||||||
<maven.compiler.source>8</maven.compiler.source>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
|
|
||||||
<java.version>1.8</java.version>
|
|
||||||
<!-- 打包跳过单元测试 -->
|
|
||||||
<skipTests>true</skipTests>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
|
||||||
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.noear</groupId>
|
|
||||||
<artifactId>solon</artifactId>
|
|
||||||
<version>${solon.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.noear</groupId>
|
|
||||||
<artifactId>solon-web</artifactId>
|
|
||||||
<version>${solon.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cloud.tianai.captcha</groupId>
|
|
||||||
<artifactId>tianai-captcha</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<source>8</source>
|
|
||||||
<target>8</target>
|
|
||||||
<compilerArgument>-parameters</compilerArgument>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon;
|
|
||||||
|
|
||||||
import cloud.tianai.captcha.solon.config.ImageCaptchaAutoConfiguration;
|
|
||||||
import cloud.tianai.captcha.solon.properties.CaptchaProperties;
|
|
||||||
import org.noear.solon.core.AppContext;
|
|
||||||
import org.noear.solon.core.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插件启动类
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.03
|
|
||||||
*/
|
|
||||||
public class XPluginImp implements Plugin {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start(AppContext context) {
|
|
||||||
context.beanMake(CaptchaProperties.class);
|
|
||||||
context.beanMake(ImageCaptchaAutoConfiguration.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-95
@@ -1,95 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.config;
|
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.ImageCaptchaApplication;
|
|
||||||
import cloud.tianai.captcha.application.TACBuilder;
|
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
|
||||||
import cloud.tianai.captcha.cache.impl.LocalCacheStore;
|
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
|
||||||
import cloud.tianai.captcha.solon.plugins.secondary.SecondaryVerificationApplication;
|
|
||||||
import cloud.tianai.captcha.solon.properties.CaptchaProperties;
|
|
||||||
import cloud.tianai.captcha.solon.service.CaptchaRedisCacheService;
|
|
||||||
import org.noear.solon.annotation.Bean;
|
|
||||||
import org.noear.solon.annotation.Configuration;
|
|
||||||
import org.noear.solon.annotation.Inject;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.03
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class ImageCaptchaAutoConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ImageCaptchaApplication imageCaptchaApplication(CaptchaProperties captchaProperties, @Inject(required = false) CaptchaRedisCacheService cacheService) {
|
|
||||||
TACBuilder tacBuilder = TACBuilder.builder();
|
|
||||||
tacBuilder.addDefaultTemplate();
|
|
||||||
tacBuilder.expire("default", captchaProperties.getExpire());
|
|
||||||
tacBuilder.prefix(captchaProperties.getPrefix());
|
|
||||||
// 注入背景图资源
|
|
||||||
if (captchaProperties.getResources().getAuto()) {
|
|
||||||
String[] split = captchaProperties.getResources().getAutoType().split(",");
|
|
||||||
List<String> wordImageClickList = captchaProperties.getResources().getImages();
|
|
||||||
for (String type : split) {
|
|
||||||
for (String path : wordImageClickList) {
|
|
||||||
tacBuilder.addResource(type, new Resource("classpath", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
List<String> wordImageClickList = captchaProperties.getResources().getWORD_IMAGE_CLICK();
|
|
||||||
if (!wordImageClickList.isEmpty()) {
|
|
||||||
for (String path : wordImageClickList) {
|
|
||||||
tacBuilder.addResource("WORD_IMAGE_CLICK", new Resource("classpath", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<String> concatList = captchaProperties.getResources().getCONCAT();
|
|
||||||
if (!concatList.isEmpty()) {
|
|
||||||
for (String path : concatList) {
|
|
||||||
tacBuilder.addResource("CONCAT", new Resource("classpath", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<String> sliderList = captchaProperties.getResources().getSLIDER();
|
|
||||||
if (!sliderList.isEmpty()) {
|
|
||||||
for (String path : sliderList) {
|
|
||||||
tacBuilder.addResource("SLIDER", new Resource("classpath", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<String> rotateList = captchaProperties.getResources().getROTATE();
|
|
||||||
if (!rotateList.isEmpty()) {
|
|
||||||
for (String path : rotateList) {
|
|
||||||
tacBuilder.addResource("ROTATE", new Resource("classpath", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注入字体包
|
|
||||||
if (null != captchaProperties.getFontPath()) {
|
|
||||||
List<String> fontPathList = captchaProperties.getFontPath();
|
|
||||||
if (!fontPathList.isEmpty()) {
|
|
||||||
for (String path : fontPathList) {
|
|
||||||
try {
|
|
||||||
tacBuilder.addFont(new Resource("classpath", path));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("读取字体包失败,path=" + path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheStore cacheStore = cacheService;
|
|
||||||
// 注入缓存器
|
|
||||||
if (null == cacheStore) {
|
|
||||||
cacheStore = new LocalCacheStore();
|
|
||||||
}
|
|
||||||
tacBuilder.setCacheStore(cacheStore);
|
|
||||||
ImageCaptchaApplication target = tacBuilder.build();
|
|
||||||
|
|
||||||
// 二次验证
|
|
||||||
if (captchaProperties.getSecondary().getEnabled()) {
|
|
||||||
target = new SecondaryVerificationApplication(target, captchaProperties, cacheStore);
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-109
@@ -1,109 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.plugins.secondary;
|
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.FilterImageCaptchaApplication;
|
|
||||||
import cloud.tianai.captcha.application.ImageCaptchaApplication;
|
|
||||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
|
||||||
import cloud.tianai.captcha.common.AnyMap;
|
|
||||||
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
|
|
||||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
|
||||||
import cloud.tianai.captcha.solon.properties.CaptchaLimit;
|
|
||||||
import cloud.tianai.captcha.solon.properties.CaptchaProperties;
|
|
||||||
import cloud.tianai.captcha.solon.properties.CaptchaSecondary;
|
|
||||||
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
|
|
||||||
import org.noear.solon.core.handle.Context;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.03
|
|
||||||
*/
|
|
||||||
public class SecondaryVerificationApplication extends FilterImageCaptchaApplication {
|
|
||||||
|
|
||||||
private final CaptchaSecondary prop;
|
|
||||||
private final CaptchaProperties captchaProperties;
|
|
||||||
private final CacheStore redisCacheService;
|
|
||||||
|
|
||||||
public SecondaryVerificationApplication(ImageCaptchaApplication target, CaptchaProperties captchaProperties, CacheStore redisCacheService) {
|
|
||||||
super(target);
|
|
||||||
this.captchaProperties = captchaProperties;
|
|
||||||
this.prop = captchaProperties.getSecondary();
|
|
||||||
this.redisCacheService = redisCacheService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApiResponse<ImageCaptchaVO> generateCaptcha(String type) {
|
|
||||||
// 检查是否每分钟超过限制
|
|
||||||
CaptchaLimit limit = captchaProperties.getLimit();
|
|
||||||
if (null != limit && limit.getEnable()) {
|
|
||||||
Context current = Context.current();
|
|
||||||
String errLimitKey = getLimitKey(current, "error");
|
|
||||||
Long errLimit = redisCacheService.getLong(errLimitKey);
|
|
||||||
if (null != errLimit && errLimit >= limit.getErrorLimit()) {
|
|
||||||
throw new ImageCaptchaException("验证次数过多,请稍后再试");
|
|
||||||
}
|
|
||||||
String reqLimitKey = getLimitKey(current, "req");
|
|
||||||
Long reqLimit = redisCacheService.getLong(reqLimitKey);
|
|
||||||
if (null != reqLimit && reqLimit >= limit.getReqLimit()) {
|
|
||||||
throw new ImageCaptchaException("获取验证码频繁,请稍后再试");
|
|
||||||
}
|
|
||||||
redisCacheService.incr(reqLimitKey, 1, 60L, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
return super.generateCaptcha(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApiResponse<?> matching(String id, ImageCaptchaTrack imageCaptchaTrack) {
|
|
||||||
ApiResponse<?> match = super.matching(id, imageCaptchaTrack);
|
|
||||||
if (match.isSuccess()) {
|
|
||||||
// 如果匹配成功, 添加二次验证记录
|
|
||||||
addSecondaryVerification(id + getRemoteId(Context.current()), imageCaptchaTrack);
|
|
||||||
} else {
|
|
||||||
CaptchaLimit limit = captchaProperties.getLimit();
|
|
||||||
if (null != limit && limit.getEnable()) {
|
|
||||||
Context current = Context.current();
|
|
||||||
String limitKey = getLimitKey(current, "error");
|
|
||||||
redisCacheService.incr(limitKey, 1, 60L, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 二次缓存验证
|
|
||||||
* @param id id
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public boolean secondaryVerification(String id) {
|
|
||||||
Map<String, Object> cache = target.getCacheStore().getAndRemoveCache(getKey(id + getRemoteId(Context.current())));
|
|
||||||
return cache != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加二次缓存验证记录
|
|
||||||
* @param id id
|
|
||||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
|
||||||
*/
|
|
||||||
protected void addSecondaryVerification(String id, ImageCaptchaTrack imageCaptchaTrack) {
|
|
||||||
target.getCacheStore().setCache(getKey(id), new AnyMap(), prop.getExpire(), TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getKey(String id) {
|
|
||||||
return prop.getKeyPrefix().concat(":").concat(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getLimitKey(Context ctx, String type) {
|
|
||||||
return prop.getKeyPrefix().concat(":limit:")
|
|
||||||
.concat(type)
|
|
||||||
.concat(":")
|
|
||||||
.concat(getRemoteId(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static String getRemoteId(Context ctx) {
|
|
||||||
return ctx.realIp() + ctx.userAgent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.properties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.04
|
|
||||||
*/
|
|
||||||
public class CaptchaLimit {
|
|
||||||
|
|
||||||
private Boolean enable;
|
|
||||||
|
|
||||||
private Long reqLimit;
|
|
||||||
|
|
||||||
private Long errorLimit;
|
|
||||||
|
|
||||||
public Boolean getEnable() {
|
|
||||||
return enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnable(Boolean enable) {
|
|
||||||
this.enable = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getReqLimit() {
|
|
||||||
return reqLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReqLimit(Long reqLimit) {
|
|
||||||
this.reqLimit = reqLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getErrorLimit() {
|
|
||||||
return errorLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setErrorLimit(Long errorLimit) {
|
|
||||||
this.errorLimit = errorLimit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-93
@@ -1,93 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.properties;
|
|
||||||
|
|
||||||
import org.noear.solon.annotation.Configuration;
|
|
||||||
import org.noear.solon.annotation.Inject;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.03
|
|
||||||
*/
|
|
||||||
@Inject("${captcha}")
|
|
||||||
@Configuration
|
|
||||||
public class CaptchaProperties {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* redis 前缀
|
|
||||||
*/
|
|
||||||
private String prefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 有效期
|
|
||||||
*/
|
|
||||||
private Long expire;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字体路径
|
|
||||||
*/
|
|
||||||
private List<String> fontPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资源路径
|
|
||||||
*/
|
|
||||||
private CaptchaResource resources;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 二次验证
|
|
||||||
*/
|
|
||||||
private CaptchaSecondary secondary;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 每分限流
|
|
||||||
*/
|
|
||||||
private CaptchaLimit limit;
|
|
||||||
|
|
||||||
public String getPrefix() {
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrefix(String prefix) {
|
|
||||||
this.prefix = prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getExpire() {
|
|
||||||
return expire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpire(Long expire) {
|
|
||||||
this.expire = expire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getFontPath() {
|
|
||||||
return fontPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFontPath(List<String> fontPath) {
|
|
||||||
this.fontPath = fontPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CaptchaResource getResources() {
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResources(CaptchaResource resources) {
|
|
||||||
this.resources = resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CaptchaSecondary getSecondary() {
|
|
||||||
return secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSecondary(CaptchaSecondary secondary) {
|
|
||||||
this.secondary = secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CaptchaLimit getLimit() {
|
|
||||||
return limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLimit(CaptchaLimit limit) {
|
|
||||||
this.limit = limit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-80
@@ -1,80 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.properties;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.03
|
|
||||||
*/
|
|
||||||
public class CaptchaResource {
|
|
||||||
|
|
||||||
private Boolean auto;
|
|
||||||
|
|
||||||
private String autoType;
|
|
||||||
|
|
||||||
private List<String> images;
|
|
||||||
|
|
||||||
private List<String> SLIDER;
|
|
||||||
|
|
||||||
private List<String> WORD_IMAGE_CLICK;
|
|
||||||
|
|
||||||
private List<String> ROTATE;
|
|
||||||
|
|
||||||
private List<String> CONCAT;
|
|
||||||
|
|
||||||
public List<String> getSLIDER() {
|
|
||||||
return SLIDER;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSLIDER(List<String> SLIDER) {
|
|
||||||
this.SLIDER = SLIDER;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getWORD_IMAGE_CLICK() {
|
|
||||||
return WORD_IMAGE_CLICK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWORD_IMAGE_CLICK(List<String> WORD_IMAGE_CLICK) {
|
|
||||||
this.WORD_IMAGE_CLICK = WORD_IMAGE_CLICK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getROTATE() {
|
|
||||||
return ROTATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setROTATE(List<String> ROTATE) {
|
|
||||||
this.ROTATE = ROTATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getCONCAT() {
|
|
||||||
return CONCAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCONCAT(List<String> CONCAT) {
|
|
||||||
this.CONCAT = CONCAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean getAuto() {
|
|
||||||
return auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAuto(Boolean auto) {
|
|
||||||
this.auto = auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAutoType() {
|
|
||||||
return autoType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAutoType(String autoType) {
|
|
||||||
this.autoType = autoType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getImages() {
|
|
||||||
return images;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImages(List<String> images) {
|
|
||||||
this.images = images;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.properties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author XT
|
|
||||||
* @Date 2024.09.03
|
|
||||||
*/
|
|
||||||
public class CaptchaSecondary {
|
|
||||||
|
|
||||||
private Boolean enabled;
|
|
||||||
|
|
||||||
private Long expire;
|
|
||||||
|
|
||||||
private String keyPrefix;
|
|
||||||
|
|
||||||
public Boolean getEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(Boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getExpire() {
|
|
||||||
return expire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpire(Long expire) {
|
|
||||||
this.expire = expire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKeyPrefix() {
|
|
||||||
return keyPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyPrefix(String keyPrefix) {
|
|
||||||
this.keyPrefix = keyPrefix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
package cloud.tianai.captcha.solon.service;
|
|
||||||
|
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
|
||||||
|
|
||||||
public interface CaptchaRedisCacheService extends CacheStore {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -17,9 +17,7 @@ import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore;
|
|||||||
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
|
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
|
||||||
import cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator;
|
import cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: 天爱有情
|
* @Author: 天爱有情
|
||||||
@@ -36,9 +34,10 @@ public class TACBuilder {
|
|||||||
private ResourceStore resourceStore;
|
private ResourceStore resourceStore;
|
||||||
private ImageTransform imageTransform;
|
private ImageTransform imageTransform;
|
||||||
// private List<FontWrapper> fontWrappers = new ArrayList<>();
|
// private List<FontWrapper> fontWrappers = new ArrayList<>();
|
||||||
private Map<String, Resource> resourceCache;
|
private Map<String, List<Resource>> resourceCache = new HashMap<>(8);
|
||||||
private Map<String, ResourceMap> templateCache;
|
private Map<String, List<ResourceMap>> templateCache = new HashMap<>(8);
|
||||||
|
|
||||||
|
private String defaultTemplatePrefix = null;
|
||||||
public static TACBuilder builder() {
|
public static TACBuilder builder() {
|
||||||
return new TACBuilder();
|
return new TACBuilder();
|
||||||
}
|
}
|
||||||
@@ -53,8 +52,7 @@ public class TACBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
||||||
DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultPathPrefix);
|
this.defaultTemplatePrefix = defaultPathPrefix;
|
||||||
defaultBuiltInResources.addDefaultTemplate(resourceStore);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,14 +153,23 @@ public class TACBuilder {
|
|||||||
if (resourceStore instanceof CrudResourceStore) {
|
if (resourceStore instanceof CrudResourceStore) {
|
||||||
CrudResourceStore crudResourceStore = (CrudResourceStore) resourceStore;
|
CrudResourceStore crudResourceStore = (CrudResourceStore) resourceStore;
|
||||||
if (!CollectionUtils.isEmpty(resourceCache)) {
|
if (!CollectionUtils.isEmpty(resourceCache)) {
|
||||||
resourceCache.forEach(crudResourceStore::addResource);
|
resourceCache.forEach((type,resources) -> {
|
||||||
resourceCache = null;
|
resources.forEach(resource -> crudResourceStore.addResource(type,resource));
|
||||||
|
});
|
||||||
|
resourceCache.clear();
|
||||||
}
|
}
|
||||||
if (!CollectionUtils.isEmpty(templateCache)) {
|
if (!CollectionUtils.isEmpty(templateCache)) {
|
||||||
templateCache.forEach(crudResourceStore::addTemplate);
|
templateCache.forEach((type, templates) -> {
|
||||||
templateCache = null;
|
templates.forEach(template -> crudResourceStore.addTemplate(type, template));
|
||||||
|
});
|
||||||
|
templateCache.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 添加默认模板
|
||||||
|
if (defaultTemplatePrefix != null) {
|
||||||
|
DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultTemplatePrefix);
|
||||||
|
defaultBuiltInResources.addDefaultTemplate(resourceStore);
|
||||||
|
}
|
||||||
if (generator == null) {
|
if (generator == null) {
|
||||||
ResourceProviders resourceProviders = new ResourceProviders();
|
ResourceProviders resourceProviders = new ResourceProviders();
|
||||||
DefaultImageCaptchaResourceManager resourceManager = new DefaultImageCaptchaResourceManager(resourceStore, resourceProviders);
|
DefaultImageCaptchaResourceManager resourceManager = new DefaultImageCaptchaResourceManager(resourceStore, resourceProviders);
|
||||||
@@ -183,16 +190,10 @@ public class TACBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cacheResource(String captchaType, Resource imageResource) {
|
private void cacheResource(String captchaType, Resource imageResource) {
|
||||||
if (resourceCache == null) {
|
resourceCache.computeIfAbsent(captchaType, k -> new ArrayList<>(4)).add(imageResource);
|
||||||
resourceCache = new LinkedHashMap<>(8);
|
|
||||||
}
|
|
||||||
resourceCache.put(captchaType, imageResource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cacheTemplate(String captchaType, ResourceMap resourceMap) {
|
private void cacheTemplate(String captchaType, ResourceMap resourceMap) {
|
||||||
if (templateCache == null) {
|
templateCache.computeIfAbsent(captchaType, k -> new ArrayList<>(4)).add(resourceMap);
|
||||||
templateCache = new LinkedHashMap<>(8);
|
|
||||||
}
|
|
||||||
templateCache.put(captchaType, resourceMap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class TACBuilderTest {
|
|||||||
|
|
||||||
ImageCaptchaApplication application = TACBuilder.builder()
|
ImageCaptchaApplication application = TACBuilder.builder()
|
||||||
// 设置资源存储器,默认是 LocalMemoryResourceStore
|
// 设置资源存储器,默认是 LocalMemoryResourceStore
|
||||||
.setResourceStore(new LocalMemoryResourceStore())
|
// .setResourceStore(new LocalMemoryResourceStore())
|
||||||
// 加载系统自带的默认资源(系统内置了几个滑块验证码缺口模板图,调用此函数加载)
|
// 加载系统自带的默认资源(系统内置了几个滑块验证码缺口模板图,调用此函数加载)
|
||||||
.addDefaultTemplate()
|
.addDefaultTemplate()
|
||||||
// 设置验证码过期时间, 单位毫秒, default 是默认验证码过期时间,当前设置为10秒,
|
// 设置验证码过期时间, 单位毫秒, default 是默认验证码过期时间,当前设置为10秒,
|
||||||
@@ -71,6 +71,7 @@ public class TACBuilderTest {
|
|||||||
// arg1 验证码类型(SLIDER、WORD_IMAGE_CLICK、ROTATE、CONCAT),
|
// arg1 验证码类型(SLIDER、WORD_IMAGE_CLICK、ROTATE、CONCAT),
|
||||||
// arg2 验证码背景图片资源
|
// arg2 验证码背景图片资源
|
||||||
.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
||||||
|
.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
||||||
.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
||||||
.addResource(CaptchaTypeConstant.ROTATE, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
.addResource(CaptchaTypeConstant.ROTATE, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
||||||
.addResource(CaptchaTypeConstant.CONCAT, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
.addResource(CaptchaTypeConstant.CONCAT, new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
||||||
|
|||||||
Reference in New Issue
Block a user