diff --git a/pom.xml b/pom.xml
index ab79290..1008997 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
cloud.tianai.captcha
tianai-captcha
- 1.5.2
+ 1.5.3
tianai-captcha
行为验证码
diff --git a/readme.md b/readme.md
index 6950efc..1f3eb06 100644
--- a/readme.md
+++ b/readme.md
@@ -27,12 +27,17 @@
> 注意: 如果你项目是使用的**Springboot**,
>
>
-请使用SpringBoot脚手架工具[tianai-captcha-springboot-starter](https://gitcode.com/tiana/tianai-captcha-springboot-starter);
+请使用SpringBoot脚手架工具
+ - [tianai-captcha-springboot-starter(gitee)](https://gitee.com/tianai/tianai-captcha-springboot-starter);
+ - [tianai-captcha-springboot-starter(gitcode)](https://gitcode.com/tiana/tianai-captcha-springboot-starter);
+ - [tianai-captcha-springboot-starter(github)](https://github.com/tianaiyouqing/tianai-captcha-springboot-starter);
>
> 该工具对tianai-captcha验证码进行了封装,使其使用更加方便快捷
-> **写好的验证码demo移步 [tianai-captcha-demo](https://gitcode.com/tiana/tianai-captcha-demo)**
+> **写好的验证码demo移步
+> - [tianai-captcha-demo(gitee)](https://gitee.com/tianai/tianai-captcha-demo)
+> - [tianai-captcha-demo(gitcode)](https://gitcode.com/tiana/tianai-captcha-demo)
### 1. 导入xml
diff --git a/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java b/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java
index 946f1b9..a9947c5 100644
--- a/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java
+++ b/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.application;
-import cloud.tianai.captcha.application.vo.CaptchaResponse;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.cache.CacheStore;
import cloud.tianai.captcha.common.AnyMap;
@@ -78,21 +77,21 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
}
@Override
- public CaptchaResponse generateCaptcha() {
+ public ApiResponse generateCaptcha() {
// 生成滑块验证码
return generateCaptcha(CaptchaTypeConstant.SLIDER);
}
@Override
- public CaptchaResponse generateCaptcha(String type) {
+ public ApiResponse generateCaptcha(String type) {
GenerateParam generateParam = new GenerateParam();
generateParam.setType(type);
return generateCaptcha(generateParam);
}
@Override
- public CaptchaResponse generateCaptcha(GenerateParam param) {
- CaptchaResponse captchaResponse = beforeGenerateCaptcha(param);
+ public ApiResponse generateCaptcha(GenerateParam param) {
+ ApiResponse captchaResponse = beforeGenerateCaptcha(param);
if (captchaResponse != null) {
return captchaResponse;
}
@@ -103,12 +102,12 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
}
@Override
- public CaptchaResponse generateCaptcha(CaptchaImageType captchaImageType) {
+ public ApiResponse generateCaptcha(CaptchaImageType captchaImageType) {
return generateCaptcha(CaptchaTypeConstant.SLIDER, captchaImageType);
}
@Override
- public CaptchaResponse generateCaptcha(String type, CaptchaImageType captchaImageType) {
+ public ApiResponse generateCaptcha(String type, CaptchaImageType captchaImageType) {
GenerateParam param = new GenerateParam();
if (CaptchaImageType.WEBP.equals(captchaImageType)) {
param.setBackgroundFormatName("webp");
@@ -122,14 +121,14 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
}
- public CaptchaResponse convertToCaptchaResponse(ImageCaptchaInfo imageCaptchaInfo) {
+ public ApiResponse convertToCaptchaResponse(ImageCaptchaInfo imageCaptchaInfo) {
if (imageCaptchaInfo == null) {
// 要是生成失败
throw new ImageCaptchaException("生成验证码失败,验证码生成为空");
}
// 生成ID
String id = generatorId(imageCaptchaInfo);
- CaptchaResponse response = beforeGenerateImageCaptchaValidData(imageCaptchaInfo);
+ ApiResponse response = beforeGenerateImageCaptchaValidData(imageCaptchaInfo);
if (response != null) {
return response;
}
@@ -151,7 +150,8 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
verificationVO.setTemplateImageWidth(imageCaptchaInfo.getTemplateImageWidth());
verificationVO.setTemplateImageHeight(imageCaptchaInfo.getTemplateImageHeight());
verificationVO.setData(imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getViewData());
- return CaptchaResponse.of(id, verificationVO);
+ verificationVO.setId(id);
+ return ApiResponse.ofSuccess(verificationVO);
}
@@ -286,15 +286,15 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
// ============== 一些模板方法 ================
- private void afterGenerateCaptcha(ImageCaptchaInfo imageCaptchaInfo, CaptchaResponse captchaResponse) {
+ private void afterGenerateCaptcha(ImageCaptchaInfo imageCaptchaInfo, ApiResponse captchaResponse) {
captchaInterceptor.afterGenerateCaptcha(captchaInterceptor.createContext(), imageCaptchaInfo.getType(), imageCaptchaInfo, captchaResponse);
}
- private CaptchaResponse beforeGenerateCaptcha(GenerateParam param) {
+ private ApiResponse beforeGenerateCaptcha(GenerateParam param) {
return captchaInterceptor.beforeGenerateCaptcha(captchaInterceptor.createContext(), param.getType(), param);
}
- private CaptchaResponse beforeGenerateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
+ private ApiResponse beforeGenerateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
return captchaInterceptor.beforeGenerateImageCaptchaValidData(captchaInterceptor.createContext(), imageCaptchaInfo.getType(), imageCaptchaInfo);
}
diff --git a/src/main/java/cloud/tianai/captcha/application/FilterImageCaptchaApplication.java b/src/main/java/cloud/tianai/captcha/application/FilterImageCaptchaApplication.java
index dcca9bb..55aedf6 100644
--- a/src/main/java/cloud/tianai/captcha/application/FilterImageCaptchaApplication.java
+++ b/src/main/java/cloud/tianai/captcha/application/FilterImageCaptchaApplication.java
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.application;
-import cloud.tianai.captcha.application.vo.CaptchaResponse;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.cache.CacheStore;
import cloud.tianai.captcha.common.response.ApiResponse;
@@ -27,27 +26,27 @@ public class FilterImageCaptchaApplication implements ImageCaptchaApplication {
}
@Override
- public CaptchaResponse generateCaptcha() {
+ public ApiResponse generateCaptcha() {
return target.generateCaptcha();
}
@Override
- public CaptchaResponse generateCaptcha(String type) {
+ public ApiResponse generateCaptcha(String type) {
return target.generateCaptcha(type);
}
@Override
- public CaptchaResponse generateCaptcha(CaptchaImageType captchaImageType) {
+ public ApiResponse generateCaptcha(CaptchaImageType captchaImageType) {
return target.generateCaptcha(captchaImageType);
}
@Override
- public CaptchaResponse generateCaptcha(String type, CaptchaImageType captchaImageType) {
+ public ApiResponse generateCaptcha(String type, CaptchaImageType captchaImageType) {
return target.generateCaptcha(type, captchaImageType);
}
@Override
- public CaptchaResponse generateCaptcha(GenerateParam param) {
+ public ApiResponse generateCaptcha(GenerateParam param) {
return target.generateCaptcha(param);
}
diff --git a/src/main/java/cloud/tianai/captcha/application/ImageCaptchaApplication.java b/src/main/java/cloud/tianai/captcha/application/ImageCaptchaApplication.java
index c751d4a..a89502b 100644
--- a/src/main/java/cloud/tianai/captcha/application/ImageCaptchaApplication.java
+++ b/src/main/java/cloud/tianai/captcha/application/ImageCaptchaApplication.java
@@ -1,7 +1,6 @@
package cloud.tianai.captcha.application;
-import cloud.tianai.captcha.application.vo.CaptchaResponse;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.cache.CacheStore;
import cloud.tianai.captcha.common.response.ApiResponse;
@@ -25,7 +24,7 @@ public interface ImageCaptchaApplication {
*
* @return
*/
- CaptchaResponse generateCaptcha();
+ ApiResponse generateCaptcha();
/**
* 生成滑块验证码
@@ -33,7 +32,7 @@ public interface ImageCaptchaApplication {
* @param type type类型
* @return CaptchaResponse
*/
- CaptchaResponse generateCaptcha(String type);
+ ApiResponse generateCaptcha(String type);
/**
* 生成滑块验证码
@@ -41,7 +40,7 @@ public interface ImageCaptchaApplication {
* @param captchaImageType 要生成webp还是jpg类型的图片
* @return CaptchaResponse
*/
- CaptchaResponse generateCaptcha(CaptchaImageType captchaImageType);
+ ApiResponse generateCaptcha(CaptchaImageType captchaImageType);
/**
* 生成验证码
@@ -50,7 +49,7 @@ public interface ImageCaptchaApplication {
* @param captchaImageType CaptchaImageType
* @return CaptchaResponse
*/
- CaptchaResponse generateCaptcha(String type, CaptchaImageType captchaImageType);
+ ApiResponse generateCaptcha(String type, CaptchaImageType captchaImageType);
/**
@@ -59,7 +58,7 @@ public interface ImageCaptchaApplication {
* @param param param
* @return CaptchaResponse
*/
- CaptchaResponse generateCaptcha(GenerateParam param);
+ ApiResponse generateCaptcha(GenerateParam param);
/**
* 匹配
diff --git a/src/main/java/cloud/tianai/captcha/application/TACBuilder.java b/src/main/java/cloud/tianai/captcha/application/TACBuilder.java
index 147e39b..a86544a 100644
--- a/src/main/java/cloud/tianai/captcha/application/TACBuilder.java
+++ b/src/main/java/cloud/tianai/captcha/application/TACBuilder.java
@@ -7,10 +7,7 @@ import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
-import cloud.tianai.captcha.resource.DefaultBuiltInResources;
-import cloud.tianai.captcha.resource.FontCache;
-import cloud.tianai.captcha.resource.ResourceProviders;
-import cloud.tianai.captcha.resource.ResourceStore;
+import cloud.tianai.captcha.resource.*;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
@@ -49,8 +46,8 @@ public class TACBuilder {
}
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
- DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultPathPrefix);
- defaultBuiltInResources.addDefaultTemplate(resourceStore);
+// DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultPathPrefix);
+// defaultBuiltInResources.addDefaultTemplate(resourceStore);
return this;
}
@@ -115,12 +112,16 @@ public class TACBuilder {
public TACBuilder addResource(String captchaType, Resource imageResource) {
- this.resourceStore.addResource(captchaType, imageResource);
+ if (resourceStore instanceof CrudResourceStore) {
+ ((CrudResourceStore) resourceStore).addResource(captchaType, imageResource);
+ }
return this;
}
public TACBuilder addTemplate(String captchaType, ResourceMap resourceMap) {
- this.resourceStore.addTemplate(captchaType, resourceMap);
+ if (resourceStore instanceof CrudResourceStore) {
+ ((CrudResourceStore) resourceStore).addTemplate(captchaType, resourceMap);
+ }
return this;
}
diff --git a/src/main/java/cloud/tianai/captcha/application/vo/ImageCaptchaVO.java b/src/main/java/cloud/tianai/captcha/application/vo/ImageCaptchaVO.java
index 0754012..1aa3b08 100644
--- a/src/main/java/cloud/tianai/captcha/application/vo/ImageCaptchaVO.java
+++ b/src/main/java/cloud/tianai/captcha/application/vo/ImageCaptchaVO.java
@@ -10,6 +10,9 @@ import java.io.Serializable;
@NoArgsConstructor
@AllArgsConstructor
public class ImageCaptchaVO implements Serializable {
+
+ /** ID.*/
+ private String id;
/** 验证码类型.*/
private String type;
/** 背景图.*/
diff --git a/src/main/java/cloud/tianai/captcha/common/constant/CommonConstant.java b/src/main/java/cloud/tianai/captcha/common/constant/CommonConstant.java
index 6d7ac63..cb36923 100644
--- a/src/main/java/cloud/tianai/captcha/common/constant/CommonConstant.java
+++ b/src/main/java/cloud/tianai/captcha/common/constant/CommonConstant.java
@@ -3,13 +3,18 @@ package cloud.tianai.captcha.common.constant;
public interface CommonConstant {
String DEFAULT_TAG = "default";
+
+
/** 图标点选资源存储类型. */
- String IMAGE_CLICK_ICON = "ICON";
+ String IMAGE_ICON = "ICON";
/** 蜂窝点选.*/
String HONEYCOMB_CLICK_ICON = "HONEYCOMB_ICON";
/** 刮刮卡图标. */
String SCRAPE_ICON = "SCRAPE_ICON";
+ // String IMAGE_CLICK_ICON = "IMAGE_CLICK_ICON";
+ String IMAGE_TIP_ICON = "IMAGE_TIP_ICON";
+ String IMAGE_CLICK_ICON = "IMAGE_CLICK_ICON";
/**
* 默认的resource资源文件路径.
diff --git a/src/main/java/cloud/tianai/captcha/common/response/ApiResponse.java b/src/main/java/cloud/tianai/captcha/common/response/ApiResponse.java
index c0c2bf3..3f92e8c 100644
--- a/src/main/java/cloud/tianai/captcha/common/response/ApiResponse.java
+++ b/src/main/java/cloud/tianai/captcha/common/response/ApiResponse.java
@@ -7,7 +7,7 @@ import java.io.Serializable;
/**
* @Author: 天爱有情
* @date 2023/4/20 9:53
- * @Description 可能是最好用的API统一返回格式类
+ * @Description API统一返回格式类
*/
@Data
@SuppressWarnings({"unchecked", "rawtypes"})
diff --git a/src/main/java/cloud/tianai/captcha/generator/common/model/dto/GenerateParam.java b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/GenerateParam.java
index 1637e80..808b02b 100644
--- a/src/main/java/cloud/tianai/captcha/generator/common/model/dto/GenerateParam.java
+++ b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/GenerateParam.java
@@ -2,7 +2,8 @@ package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.AnyMap;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
-import lombok.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
/**
* @Author: 天爱有情
diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java
index 92ccb4c..3e674a7 100644
--- a/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java
+++ b/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java
@@ -28,6 +28,10 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
protected Map imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4);
protected Map imageCaptchaGeneratorProviderMap = new HashMap<>(4);
+ // 点选类验证码字体
+// @Setter
+// @Getter
+// protected List fontWrappers;
@Setter
@Getter
private String defaultCaptcha = SLIDER;
diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java
index b61ee70..e4ced7b 100644
--- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java
+++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java
@@ -88,14 +88,11 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(activeTemplate.getWidth(), background.getHeight());
CaptchaImageUtils.overlayImage(matrixTemplate, cutImage, 0, randomY);
- XandY xandY = new XandY();
- xandY.x = randomX;
- xandY.y = randomY;
captchaExchange.setBackgroundImage(background);
captchaExchange.setTemplateImage(matrixTemplate);
captchaExchange.setTemplateResource(templateResource);
captchaExchange.setResourceImage(resourceImage);
- captchaExchange.setTransferData(xandY);
+ captchaExchange.setTransferData(new Point(randomX,randomY));
// 后处理
// applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, captchaExchange);
@@ -125,11 +122,6 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
}
- public static class XandY {
- int x;
- int y;
- }
-
@SneakyThrows
@Override
public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
@@ -139,7 +131,7 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
Resource resourceImage = captchaExchange.getResourceImage();
ResourceMap templateResource = captchaExchange.getTemplateResource();
CustomData customData = captchaExchange.getCustomData();
- XandY data = (XandY) captchaExchange.getTransferData();
+ Point data = (Point) captchaExchange.getTransferData();
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData);
SliderImageCaptchaInfo imageCaptchaInfo = SliderImageCaptchaInfo.of(data.x, data.y,
diff --git a/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptor.java b/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptor.java
index b699565..207d78e 100644
--- a/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptor.java
+++ b/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptor.java
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.interceptor;
-import cloud.tianai.captcha.application.vo.CaptchaResponse;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.common.AnyMap;
import cloud.tianai.captcha.common.response.ApiResponse;
@@ -41,18 +40,18 @@ public interface CaptchaInterceptor {
return new Context(getName(), null, -1, 1, EmptyCaptchaInterceptor.INSTANCE);
}
- default CaptchaResponse beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
+ default ApiResponse beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
return null;
}
- default CaptchaResponse beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
+ default ApiResponse beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
return null;
}
default void afterGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, AnyMap validData) {
}
- default void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, CaptchaResponse captchaResponse) {
+ default void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, ApiResponse captchaResponse) {
}
default ApiResponse> beforeValid(Context context, String type, MatchParam matchParam, AnyMap validData) {
diff --git a/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptorGroup.java b/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptorGroup.java
index 449f7db..48b1b1b 100644
--- a/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptorGroup.java
+++ b/src/main/java/cloud/tianai/captcha/interceptor/CaptchaInterceptorGroup.java
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.interceptor;
-import cloud.tianai.captcha.application.vo.CaptchaResponse;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.common.AnyMap;
import cloud.tianai.captcha.common.response.ApiResponse;
@@ -66,9 +65,9 @@ public class CaptchaInterceptorGroup implements CaptchaInterceptor {
}
@Override
- public CaptchaResponse beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
+ public ApiResponse beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
context = createContextIfNecessary(context);
- CaptchaResponse captchaResponse = null;
+ ApiResponse captchaResponse = null;
while (context.next() < context.getCount()) {
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
captchaResponse = interceptor.beforeGenerateCaptcha(context, type, param);
@@ -78,7 +77,7 @@ public class CaptchaInterceptorGroup implements CaptchaInterceptor {
}
@Override
- public void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, CaptchaResponse captchaResponse) {
+ public void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, ApiResponse captchaResponse) {
context = createContextIfNecessary(context);
while (context.next() < context.getCount()) {
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
@@ -111,9 +110,9 @@ public class CaptchaInterceptorGroup implements CaptchaInterceptor {
}
@Override
- public CaptchaResponse beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
+ public ApiResponse beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
context = createContextIfNecessary(context);
- CaptchaResponse captchaResponse = null;
+ ApiResponse captchaResponse = null;
while (context.next() < context.getCount()) {
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
captchaResponse = interceptor.beforeGenerateImageCaptchaValidData(context, type, imageCaptchaInfo);
diff --git a/src/main/java/cloud/tianai/captcha/interceptor/impl/BasicTrackCaptchaInterceptor.java b/src/main/java/cloud/tianai/captcha/interceptor/impl/BasicTrackCaptchaInterceptor.java
index 2838c65..8217f4d 100644
--- a/src/main/java/cloud/tianai/captcha/interceptor/impl/BasicTrackCaptchaInterceptor.java
+++ b/src/main/java/cloud/tianai/captcha/interceptor/impl/BasicTrackCaptchaInterceptor.java
@@ -17,7 +17,7 @@ import java.util.List;
* @Description BasicCaptchaTrackValidator
*/
public class BasicTrackCaptchaInterceptor implements CaptchaInterceptor {
- public static final CodeDefinition DEFINITION = new CodeDefinition(50001, "basic check fail");
+ public static final CodeDefinition DEFINITION = new CodeDefinition(50001, "basic track check fail");
@Override
public String getName() {
diff --git a/src/main/java/cloud/tianai/captcha/resource/AbstractResourceStore.java b/src/main/java/cloud/tianai/captcha/resource/AbstractResourceStore.java
deleted file mode 100644
index cc1796f..0000000
--- a/src/main/java/cloud/tianai/captcha/resource/AbstractResourceStore.java
+++ /dev/null
@@ -1,203 +0,0 @@
-package cloud.tianai.captcha.resource;
-
-import cloud.tianai.captcha.resource.common.model.dto.Resource;
-import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
-import lombok.Getter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class AbstractResourceStore implements ResourceStore {
-
- @Getter
- protected ChainListener listener = new ChainListener();
-
- boolean isInit = false;
-
- @Override
- public void init(ImageCaptchaResourceManager resourceManager) {
- if (isInit) {
- return;
- }
- doInit();
- isInit = true;
- listener.onInit(this, resourceManager);
- }
-
- @Override
- public void addListener(ResourceListener listener) {
- this.listener.removeListener(listener);
- this.listener.addListener(listener);
- }
-
- @Override
- public void addResource(String type, Resource resource) {
- doAddResource(type, resource);
- if (isInit) {
- listener.onAddResource(type, resource);
- }
- }
-
- @Override
- public void addTemplate(String type, ResourceMap template) {
- doAddTemplate(type, template);
- if (isInit) {
- listener.onAddTemplate(type, template);
- }
- }
-
- @Override
- public Resource deleteResource(String type, String id) {
- Resource resource = doDeleteResource(type, id);
- if (isInit && resource != null) {
- listener.onDeleteResource(type, resource);
- }
- return resource;
- }
-
- @Override
- public ResourceMap deleteTemplate(String type, String id) {
- ResourceMap resourceMap = doDeleteTemplate(type, id);
- if (isInit && resourceMap != null) {
- listener.onDeleteTemplate(type, resourceMap);
- }
- return resourceMap;
- }
-
- @Override
- public Resource randomGetResourceByTypeAndTag(String type, String tag) {
- Resource resource = doRandomGetResourceByTypeAndTag(type, tag);
- if (isInit && resource != null) {
- listener.onRandomGetResourceByTypeAndTag(type, tag, resource);
- }
- return resource;
- }
-
- @Override
- public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) {
- ResourceMap resourceMap = doRandomGetTemplateByTypeAndTag(type, tag);
- if (isInit && resourceMap != null) {
- listener.onRandomGetTemplateByTypeAndTag(type, tag, resourceMap);
- }
- return resourceMap;
- }
-
-
- @Override
- public void clearAllResources() {
- doClearAllResources();
- if (isInit) {
- listener.onClearAllResources();
- }
- }
-
- @Override
- public void clearAllTemplates() {
- doClearAllTemplates();
- if (isInit) {
- listener.onClearAllTemplates();
- }
- }
-
- public void doInit() {
-
- }
-
- public abstract void doClearAllResources();
-
- public abstract void doClearAllTemplates();
-
- public abstract Resource doRandomGetResourceByTypeAndTag(String type, String tag);
-
- public abstract ResourceMap doRandomGetTemplateByTypeAndTag(String type, String tag);
-
- public abstract ResourceMap doDeleteTemplate(String type, String id);
-
- public abstract Resource doDeleteResource(String type, String id);
-
- public abstract void doAddResource(String type, Resource resource);
-
- public abstract void doAddTemplate(String type, ResourceMap template);
-
-
- public static class ChainListener implements ResourceListener {
- protected List listeners = new ArrayList<>();
-
- public ChainListener() {
-
- }
-
- public void addListener(ResourceListener listener) {
- listeners.add(listener);
- }
-
- public void removeListener(ResourceListener listener) {
- listeners.remove(listener);
- }
-
- @Override
- public void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
- for (ResourceListener listener : listeners) {
- listener.onInit(resourceStore, resourceManager);
- }
- }
-
- @Override
- public void onAddResource(String type, Resource resource) {
- for (ResourceListener listener : listeners) {
- listener.onAddResource(type, resource);
- }
- }
-
- @Override
- public void onAddTemplate(String type, ResourceMap template) {
- for (ResourceListener listener : listeners) {
- listener.onAddTemplate(type, template);
- }
- }
-
- @Override
- public void onClearAllResources() {
- for (ResourceListener listener : listeners) {
- listener.onClearAllResources();
- }
- }
-
- @Override
- public void onClearAllTemplates() {
- for (ResourceListener listener : listeners) {
- listener.onClearAllTemplates();
- }
- }
-
- @Override
- public void onRandomGetResourceByTypeAndTag(String type, String tag, Resource resource) {
- for (ResourceListener listener : listeners) {
- listener.onRandomGetResourceByTypeAndTag(type, tag, resource);
- }
- }
-
- @Override
- public void onRandomGetTemplateByTypeAndTag(String type, String tag, ResourceMap template) {
- for (ResourceListener listener : listeners) {
- listener.onRandomGetTemplateByTypeAndTag(type, tag, template);
- }
- }
-
- @Override
- public void onDeleteResource(String type, Resource resource) {
- for (ResourceListener listener : listeners) {
- listener.onDeleteResource(type, resource);
- }
- }
-
- @Override
- public void onDeleteTemplate(String type, ResourceMap template) {
- for (ResourceListener listener : listeners) {
- listener.onDeleteTemplate(type, template);
- }
- }
-
-
- }
-}
diff --git a/src/main/java/cloud/tianai/captcha/resource/CrudResourceStore.java b/src/main/java/cloud/tianai/captcha/resource/CrudResourceStore.java
new file mode 100644
index 0000000..a6621c7
--- /dev/null
+++ b/src/main/java/cloud/tianai/captcha/resource/CrudResourceStore.java
@@ -0,0 +1,77 @@
+package cloud.tianai.captcha.resource;
+
+import cloud.tianai.captcha.resource.common.model.dto.Resource;
+import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
+
+import java.util.List;
+
+/**
+ * @Author: 天爱有情
+ * @date 2025/6/13 16:43
+ * @Description 具有CRUD属性的资源存储器
+ */
+public interface CrudResourceStore extends ResourceStore {
+ /**
+ * 添加资源
+ *
+ * @param type 验证码类型
+ * @param resource 资源
+ */
+ void addResource(String type, Resource resource);
+
+
+ /**
+ * 添加模板
+ *
+ * @param type 验证码类型
+ * @param template 模板
+ */
+ void addTemplate(String type, ResourceMap template);
+
+ /**
+ * 删除资源
+ *
+ * @param type 验证码类型
+ * @param id 资源ID
+ * @return Resource
+ */
+ Resource deleteResource(String type, String id);
+
+ /**
+ * 删除模板
+ *
+ * @param type 验证码类型
+ * @param id 资源ID
+ * @return ResourceMap
+ */
+ ResourceMap deleteTemplate(String type, String id);
+
+ /**
+ * 获取某个资源列表
+ *
+ * @param type 验证码类型
+ * @param tag 资源标签(可为空)
+ * @return List
+ */
+ List listResourcesByTypeAndTag(String type, String tag);
+
+ /**
+ * 获取某个模板列表
+ *
+ * @param type 验证码类型
+ * @param tag 资源标签(可为空)
+ * @return List
+ */
+ List listTemplatesByTypeAndTag(String type, String tag);
+
+
+ /**
+ * 清除所有内置模板
+ */
+ void clearAllTemplates();
+
+ /**
+ * 清除所有内置资源
+ */
+ void clearAllResources();
+}
diff --git a/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java b/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java
index e4a4b7b..1636bf9 100644
--- a/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java
+++ b/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java
@@ -23,7 +23,7 @@ public class DefaultBuiltInResources {
public static final String PATH_PREFIX = "classpath:META-INF/cut-image/template";
- private static Map> defaultTemplateResource = new HashMap<>(8);
+ private static Map> defaultTemplateResource = new HashMap<>(8);
public DefaultBuiltInResources(String defaultPathPrefix) {
@@ -75,17 +75,21 @@ public class DefaultBuiltInResources {
public void addDefaultTemplate(String type, ResourceStore resourceStore) {
- Consumer resourceStoreConsumer = defaultTemplateResource.get(type);
- if (resourceStoreConsumer == null) {
- return;
+ if (resourceStore instanceof CrudResourceStore) {
+ Consumer resourceStoreConsumer = defaultTemplateResource.get(type);
+ if (resourceStoreConsumer == null) {
+ return;
+ }
+ resourceStoreConsumer.accept((CrudResourceStore) resourceStore);
}
- resourceStoreConsumer.accept(resourceStore);
}
public void addDefaultTemplate(ResourceStore resourceStore) {
- defaultTemplateResource.forEach((type, consumer) -> {
- consumer.accept(resourceStore);
- });
+ if (resourceStore instanceof CrudResourceStore) {
+ defaultTemplateResource.forEach((type, consumer) -> {
+ consumer.accept((CrudResourceStore) resourceStore);
+ });
+ }
}
}
diff --git a/src/main/java/cloud/tianai/captcha/resource/FontCache.java b/src/main/java/cloud/tianai/captcha/resource/FontCache.java
index bd6a007..2c46512 100644
--- a/src/main/java/cloud/tianai/captcha/resource/FontCache.java
+++ b/src/main/java/cloud/tianai/captcha/resource/FontCache.java
@@ -2,7 +2,7 @@ package cloud.tianai.captcha.resource;
import cloud.tianai.captcha.generator.common.FontWrapper;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
-import lombok.Data;
+import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -20,7 +20,7 @@ import java.util.concurrent.ConcurrentHashMap;
* @Description 一个用于统一缓存字体文件的对象
*/
@Slf4j
-public class FontCache implements ResourceListener {
+public class FontCache implements ResourceStore {
public static final String FONT_TYPE = "font";
@@ -31,19 +31,20 @@ public class FontCache implements ResourceListener {
@Setter
@Getter
private int fontSize = 70;
- public static FontCache getInstance() {
- return INSTANCE.INSTANCE;
- }
- public FontCache() {
+
+
+ public FontCache(ResourceStore resourceStore) {
+ this.resourceStore = resourceStore;
}
@Override
- public void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
- this.resourceStore = resourceStore;
+ public void init(ImageCaptchaResourceManager resourceManager) {
+ resourceStore.init(resourceManager);
this.resourceManager = resourceManager;
}
+
public FontWrapper getFont(Resource resource) {
try (InputStream stream = resourceManager.getResourceInputStream(resource)) {
Font font = Font.createFont(0, stream);
@@ -53,43 +54,27 @@ public class FontCache implements ResourceListener {
}
}
+
+ private String calcId(Resource resource) {
+ // 缓存id, 避免重复加载。 多个验证码可能使用同一个字体, 这里不使用资源ID作为缓存ID, 而是使用type+data作为缓存ID。
+ return resource.getType() + "_" + resource.getData();
+ }
+
@Override
- public void onAddResource(String type, Resource resource) {
+ public List randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
+ List resources = resourceStore.randomGetResourceByTypeAndTag(type, tag, quantity);
+ // 字体增强
if (FONT_TYPE.equalsIgnoreCase(type)) {
- fontMap.computeIfAbsent(resource.getId(), v -> getFont(resource));
+ for (Resource resource : resources) {
+ FontWrapper fontWrapper = fontMap.computeIfAbsent(calcId(resource), v -> getFont(resource));
+ resource.setExtra(fontWrapper);
+ }
}
+ return resources;
}
@Override
- public void onDeleteResource(String type, Resource resource) {
- if (FONT_TYPE.equalsIgnoreCase(type)) {
- fontMap.remove(resource.getId());
- }
- }
-
- @Override
- public void onClearAllResources() {
- fontMap.clear();
- }
-
- @Override
- public void onRandomGetResourceByTypeAndTag(String type, String tag, Resource resource) {
- if (FONT_TYPE.equalsIgnoreCase(type)) {
- FontWrapper fontWrapper = fontMap.computeIfAbsent(resource.getId(), v -> getFont(resource));
-
- resource.setExtra(fontWrapper);
- }
- }
-
- public void loadAllFonts() {
- List resources = resourceStore.listResourcesByTypeAndTag(FONT_TYPE, null);
- for (Resource resource : resources) {
- fontMap.computeIfAbsent(resource.getId(), v -> getFont(resource));
- }
- }
-
-
- private static class INSTANCE {
- private static final FontCache INSTANCE = new FontCache();
+ public List randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
+ return resourceStore.randomGetTemplateByTypeAndTag(type, tag, quantity);
}
}
diff --git a/src/main/java/cloud/tianai/captcha/resource/ImageCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/resource/ImageCaptchaResourceManager.java
index 6ff62d9..255237c 100644
--- a/src/main/java/cloud/tianai/captcha/resource/ImageCaptchaResourceManager.java
+++ b/src/main/java/cloud/tianai/captcha/resource/ImageCaptchaResourceManager.java
@@ -31,6 +31,27 @@ public interface ImageCaptchaResourceManager {
*/
Resource randomGetResource(String type, String tag);
+
+ /**
+ * 随机获取某个模板
+ *
+ * @param type 验证码类型
+ * @param tag 二级过滤,可以为空
+ * @param quantity 一次性获取的数量
+ * @return Map
+ */
+ List randomGetTemplate(String type, String tag, Integer quantity);
+
+ /**
+ * 随机获取某个资源对象
+ *
+ * @param type 验证码类型
+ * @param tag 二级过滤,可以为空
+ * @param quantity 一次性获取的数量
+ * @return Resource
+ */
+ List randomGetResource(String type, String tag, Integer quantity);
+
/**
* 获取真正的资源流通过资源对象
*
diff --git a/src/main/java/cloud/tianai/captcha/resource/ResourceListener.java b/src/main/java/cloud/tianai/captcha/resource/ResourceListener.java
deleted file mode 100644
index 09a14fc..0000000
--- a/src/main/java/cloud/tianai/captcha/resource/ResourceListener.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cloud.tianai.captcha.resource;
-
-import cloud.tianai.captcha.resource.common.model.dto.Resource;
-import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
-
-/**
- * @Author: 天爱有情
- * @date 2024/11/19 9:26
- * @Description 此类负责对 ResourceStore 进行一下扩展增强, 对ResourceStore的相关方法添加一个hook回调
- */
-public interface ResourceListener {
-
- default void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
- }
-
-
- default void onAddResource(String type, Resource resource) {
-
- }
-
- default void onAddTemplate(String type, ResourceMap template) {
-
- }
-
- default void onDeleteResource(String type, Resource resource) {
-
- }
-
- default void onDeleteTemplate(String type, ResourceMap template) {
-
- }
-
- default void onClearAllResources() {
-
- }
-
- default void onClearAllTemplates() {
-
- }
-
- default void onRandomGetResourceByTypeAndTag(String type, String tag, Resource resource) {
-
- }
-
- default void onRandomGetTemplateByTypeAndTag(String type, String tag, ResourceMap template) {
-
- }
-
-
-}
diff --git a/src/main/java/cloud/tianai/captcha/resource/ResourceStore.java b/src/main/java/cloud/tianai/captcha/resource/ResourceStore.java
index 4d29c3b..069e17a 100644
--- a/src/main/java/cloud/tianai/captcha/resource/ResourceStore.java
+++ b/src/main/java/cloud/tianai/captcha/resource/ResourceStore.java
@@ -13,65 +13,6 @@ import java.util.List;
public interface ResourceStore {
void init(ImageCaptchaResourceManager resourceManager);
- /**
- * 给ResourceStore添加hook,用于一些扩展
- *
- * @param hook
- */
- void addListener(ResourceListener hook);
-
- /**
- * 添加资源
- *
- * @param type 验证码类型
- * @param resource 资源
- */
- void addResource(String type, Resource resource);
-
-
- /**
- * 添加模板
- *
- * @param type 验证码类型
- * @param template 模板
- */
- void addTemplate(String type, ResourceMap template);
-
- /**
- * 删除资源
- *
- * @param type 验证码类型
- * @param id 资源ID
- * @return Resource
- */
- Resource deleteResource(String type, String id);
-
- /**
- * 删除模板
- *
- * @param type 验证码类型
- * @param id 资源ID
- * @return ResourceMap
- */
- ResourceMap deleteTemplate(String type, String id);
-
- /**
- * 获取某个资源列表
- *
- * @param type 验证码类型
- * @param tag 资源标签(可为空)
- * @return List
- */
- List listResourcesByTypeAndTag(String type, String tag);
-
- /**
- * 获取某个模板列表
- *
- * @param type 验证码类型
- * @param tag 资源标签(可为空)
- * @return List
- */
- List listTemplatesByTypeAndTag(String type, String tag);
/**
* 随机获取某个资源
@@ -79,7 +20,7 @@ public interface ResourceStore {
* @param type type
* @return Resource
*/
- Resource randomGetResourceByTypeAndTag(String type, String tag);
+ List randomGetResourceByTypeAndTag(String type, String tag, Integer quantity);
/**
* 随机获取某个模板通过type
@@ -87,16 +28,5 @@ public interface ResourceStore {
* @param type type
* @return Map
*/
- ResourceMap randomGetTemplateByTypeAndTag(String type, String tag);
-
- /**
- * 清除所有内置模板
- */
- void clearAllTemplates();
-
- /**
- * 清除所有内置资源
- */
- void clearAllResources();
-
+ List randomGetTemplateByTypeAndTag(String type, String tag,Integer quantity);
}
diff --git a/src/main/java/cloud/tianai/captcha/resource/impl/DefaultImageCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/resource/impl/DefaultImageCaptchaResourceManager.java
index ef554cb..f34d635 100644
--- a/src/main/java/cloud/tianai/captcha/resource/impl/DefaultImageCaptchaResourceManager.java
+++ b/src/main/java/cloud/tianai/captcha/resource/impl/DefaultImageCaptchaResourceManager.java
@@ -1,12 +1,15 @@
package cloud.tianai.captcha.resource.impl;
+import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.resource.*;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import lombok.Getter;
import java.io.InputStream;
+import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* @Author: 天爱有情
@@ -36,28 +39,43 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
this.resourceStore = new LocalMemoryResourceStore();
}
// 在这里临时加上字体缓存器
- resourceStore.addListener(FontCache.getInstance());
+ resourceStore = new FontCache(resourceStore);
resourceStore.init(this);
}
@Override
public ResourceMap randomGetTemplate(String type, String tag) {
- ResourceMap resourceMap = resourceStore.randomGetTemplateByTypeAndTag(type, tag);
- if (resourceMap == null) {
- throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
- }
- return resourceMap;
+ return randomGetTemplate(type, tag, 1).get(0);
}
@Override
public Resource randomGetResource(String type, String tag) {
- Resource resource = resourceStore.randomGetResourceByTypeAndTag(type, tag);
- if (resource == null) {
- throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
- }
- return resource;
+ return randomGetResource(type, tag, 1).get(0);
}
+ @Override
+ public List randomGetTemplate(String type, String tag, Integer quantity) {
+ List resourceMaps = resourceStore.randomGetTemplateByTypeAndTag(type, tag, quantity);
+ if (CollectionUtils.isEmpty(resourceMaps) || resourceMaps.size() != quantity) {
+ throw new IllegalStateException("随机获取**模板**错误,获取到的数量和指定数量不一致," +
+ " 指定获取数量[" + quantity + "],获取到的数据:[" + Optional.ofNullable(resourceMaps).orElse(Collections.emptyList()).size() + "], " +
+ "[type:" + type + ",tag:" + tag + "]");
+ }
+ return resourceMaps;
+ }
+
+ @Override
+ public List randomGetResource(String type, String tag, Integer quantity) {
+ List resources = resourceStore.randomGetResourceByTypeAndTag(type, tag, quantity);
+ if (CollectionUtils.isEmpty(resources) || resources.size() != quantity) {
+ throw new IllegalStateException("随机获取**资源**错误,获取到的数量和指定数量不一致," +
+ " 指定获取数量[" + quantity + "],获取到的数据:[" + Optional.ofNullable(resources).orElse(Collections.emptyList()).size() + "], " +
+ "[type:" + type + ",tag:" + tag + "]");
+ }
+ return resources;
+ }
+
+
@Override
public InputStream getResourceInputStream(Resource resource) {
return resourceProviders.getResourceInputStream(resource);
diff --git a/src/main/java/cloud/tianai/captcha/resource/impl/LocalMemoryResourceStore.java b/src/main/java/cloud/tianai/captcha/resource/impl/LocalMemoryResourceStore.java
index ea31f43..fd047b3 100644
--- a/src/main/java/cloud/tianai/captcha/resource/impl/LocalMemoryResourceStore.java
+++ b/src/main/java/cloud/tianai/captcha/resource/impl/LocalMemoryResourceStore.java
@@ -3,7 +3,8 @@ package cloud.tianai.captcha.resource.impl;
import cloud.tianai.captcha.common.constant.CommonConstant;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.ObjectUtils;
-import cloud.tianai.captcha.resource.AbstractResourceStore;
+import cloud.tianai.captcha.resource.CrudResourceStore;
+import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
@@ -15,43 +16,52 @@ import java.util.concurrent.ThreadLocalRandom;
* @date 2021/8/7 15:43
* @Description 默认的资源存储
*/
-public class LocalMemoryResourceStore extends AbstractResourceStore {
- private static final String TYPE_TAG_SPLIT_FLAG = "|";
-
+public class LocalMemoryResourceStore implements CrudResourceStore {
/** 用于检索 type和tag. */
- private Map> templateResourceTagMap = new HashMap<>(2);
- private Map> resourceTagMap = new HashMap<>(2);
+ private final Map>> templateResourceTagMap = new HashMap<>(2);
+ private final Map>> resourceTagMap = new HashMap<>(2);
+
+
+ private void ensureTypeTagMapExists(Map>> map, String type, String tag) {
+ map.computeIfAbsent(type, k -> new HashMap<>())
+ .computeIfAbsent(tag, k -> new ArrayList<>(20));
+ }
+
+ private void ensureTypeTagMapExistsForTemplate(Map>> map, String type, String tag) {
+ map.computeIfAbsent(type, k -> new HashMap<>())
+ .computeIfAbsent(tag, k -> new ArrayList<>(2));
+ }
@Override
- public void doAddResource(String type, Resource resource) {
+ public void addResource(String type, Resource resource) {
if (ObjectUtils.isEmpty(resource.getTag())) {
resource.setTag(CommonConstant.DEFAULT_TAG);
}
- resourceTagMap.computeIfAbsent(mergeTypeAndTag(type, resource.getTag()), k -> new ArrayList<>(20)).add(resource);
+ ensureTypeTagMapExists(resourceTagMap, type, resource.getTag());
+ resourceTagMap.get(type).get(resource.getTag()).add(resource);
}
@Override
- public void doAddTemplate(String type, ResourceMap template) {
+ public void addTemplate(String type, ResourceMap template) {
if (ObjectUtils.isEmpty(template.getTag())) {
template.setTag(CommonConstant.DEFAULT_TAG);
}
- templateResourceTagMap.computeIfAbsent(mergeTypeAndTag(type, template.getTag()), k -> new ArrayList<>(2)).add(template);
+ ensureTypeTagMapExistsForTemplate(templateResourceTagMap, type, template.getTag());
+ templateResourceTagMap.get(type).get(template.getTag()).add(template);
}
@Override
- public Resource doDeleteResource(String type, String id) {
- for (Map.Entry> entry : resourceTagMap.entrySet()) {
- String k = entry.getKey();
- List v = entry.getValue();
- String splitType = splitTypeTag(k)[0];
- if (splitType.equals(type)) {
- Iterator iterator = v.iterator();
- while (iterator.hasNext()) {
- Resource next = iterator.next();
- if (next.getId().equals(id)) {
- iterator.remove();
- return next;
- }
+ public Resource deleteResource(String type, String id) {
+ Map> tagMap = resourceTagMap.get(type);
+ if (tagMap == null) return null;
+
+ for (List resources : tagMap.values()) {
+ Iterator iterator = resources.iterator();
+ while (iterator.hasNext()) {
+ Resource res = iterator.next();
+ if (res.getId().equals(id)) {
+ iterator.remove();
+ return res;
}
}
}
@@ -59,19 +69,17 @@ public class LocalMemoryResourceStore extends AbstractResourceStore {
}
@Override
- public ResourceMap doDeleteTemplate(String type, String id) {
- for (Map.Entry> entry : templateResourceTagMap.entrySet()) {
- String k = entry.getKey();
- List v = entry.getValue();
- String splitType = splitTypeTag(k)[0];
- if (splitType.equals(type)) {
- Iterator iterator = v.iterator();
- while (iterator.hasNext()) {
- ResourceMap next = iterator.next();
- if (next.getId().equals(id)) {
- iterator.remove();
- return next;
- }
+ public ResourceMap deleteTemplate(String type, String id) {
+ Map> tagMap = templateResourceTagMap.get(type);
+ if (tagMap == null) return null;
+
+ for (List templates : tagMap.values()) {
+ Iterator iterator = templates.iterator();
+ while (iterator.hasNext()) {
+ ResourceMap temp = iterator.next();
+ if (temp.getId().equals(id)) {
+ iterator.remove();
+ return temp;
}
}
}
@@ -81,135 +89,97 @@ public class LocalMemoryResourceStore extends AbstractResourceStore {
@Override
public List listResourcesByTypeAndTag(String type, String tag) {
if (!ObjectUtils.isEmpty(tag)) {
- return resourceTagMap.get(mergeTypeAndTag(type, tag));
+ Map> tagMap = resourceTagMap.get(type);
+ return tagMap == null ? Collections.emptyList() : tagMap.getOrDefault(tag, Collections.emptyList());
}
- List resourceList = new ArrayList<>();
- resourceTagMap.forEach((k, v) -> {
- String splitType = splitTypeTag(k)[0];
- if (splitType.equals(type)) {
- resourceList.addAll(v);
+ List result = new ArrayList<>();
+ Map> tagMap = resourceTagMap.get(type);
+ if (tagMap != null) {
+ for (List list : tagMap.values()) {
+ result.addAll(list);
}
- });
- return resourceList;
+ }
+ return result;
}
@Override
public List listTemplatesByTypeAndTag(String type, String tag) {
if (!ObjectUtils.isEmpty(tag)) {
- return templateResourceTagMap.get(mergeTypeAndTag(type, tag));
+ Map> tagMap = templateResourceTagMap.get(type);
+ return tagMap == null ? Collections.emptyList() : tagMap.getOrDefault(tag, Collections.emptyList());
}
- List resourceMapList = new ArrayList<>();
- templateResourceTagMap.forEach((k, v) -> {
- String splitType = splitTypeTag(k)[0];
- if (splitType.equals(type)) {
- resourceMapList.addAll(v);
+ List result = new ArrayList<>();
+ Map> tagMap = templateResourceTagMap.get(type);
+ if (tagMap != null) {
+ for (List list : tagMap.values()) {
+ result.addAll(list);
}
- });
- return resourceMapList;
+ }
+ return result;
}
@Override
- public Resource doRandomGetResourceByTypeAndTag(String type, String tag) {
- List resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
+ public void init(ImageCaptchaResourceManager resourceManager) {
+
+ }
+
+ @Override
+ public List randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
+ List resources = listResourcesByTypeAndTag(type, tag);
if (CollectionUtils.isEmpty(resources)) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
}
- if (resources.size() == 1) {
- return resources.get(0);
+ int size = resources.size();
+ if (quantity > size) {
+ throw new IllegalArgumentException("请求的资源数量超过可用资源总数");
}
- int randomIndex = ThreadLocalRandom.current().nextInt(resources.size());
- try {
- return resources.get(randomIndex);
- } catch (IndexOutOfBoundsException e) {
- try {
- Thread.sleep(0);
- } catch (InterruptedException ex) {
- // ignore
- }
- return doRandomGetResourceByTypeAndTag(type, tag);
+
+ Set indexes = new HashSet<>(quantity);
+ while (indexes.size() < quantity) {
+ indexes.add(ThreadLocalRandom.current().nextInt(size));
}
+
+ List result = new ArrayList<>(quantity);
+ for (int index : indexes) {
+ result.add(resources.get(index));
+ }
+ return result;
}
+
@Override
- public ResourceMap doRandomGetTemplateByTypeAndTag(String type, String tag) {
- List templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
- if (CollectionUtils.isEmpty(templateList)) {
+ public List randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
+ List templates = listTemplatesByTypeAndTag(type, tag);
+ if (CollectionUtils.isEmpty(templates)) {
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
}
- if (templateList.size() == 1) {
- return templateList.get(0);
+ int size = templates.size();
+ if (quantity > size) {
+ throw new IllegalArgumentException("请求的模板数量超过可用模板总数");
}
- int randomIndex = ThreadLocalRandom.current().nextInt(templateList.size());
- try {
- return templateList.get(randomIndex);
- } catch (IndexOutOfBoundsException e) {
- try {
- Thread.sleep(0);
- } catch (InterruptedException ex) {
- // ignore
- }
- return doRandomGetTemplateByTypeAndTag(type, tag);
+
+ Set indexes = new HashSet<>(quantity);
+ while (indexes.size() < quantity) {
+ indexes.add(ThreadLocalRandom.current().nextInt(size));
}
- }
- public String mergeTypeAndTag(String type, String tag) {
- if (tag == null) {
- tag = CommonConstant.DEFAULT_TAG;
+ List result = new ArrayList<>(quantity);
+ for (int index : indexes) {
+ result.add(templates.get(index));
}
- return type + TYPE_TAG_SPLIT_FLAG + tag;
- }
-
- public String[] splitTypeTag(String k) {
- return k.split("\\" + TYPE_TAG_SPLIT_FLAG);
- }
-
-
- public void clearResources(String type, String tag) {
- resourceTagMap.remove(mergeTypeAndTag(type, tag));
+ return result;
}
@Override
- public void doClearAllResources() {
+ public void clearAllResources() {
resourceTagMap.clear();
}
- public Map> listAllResources() {
- return resourceTagMap;
- }
-
- public List listResourcesByType(String type, String tag) {
- return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
- }
-
- public int getAllResourceCount() {
- int count = 0;
- for (List value : resourceTagMap.values()) {
- count += value.size();
- }
- return count;
- }
-
- public int getResourceCount(String type, String tag) {
- return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList()).size();
- }
-
@Override
- public void doClearAllTemplates() {
+ public void clearAllTemplates() {
templateResourceTagMap.clear();
}
- public void clearTemplates(String type, String tag) {
- templateResourceTagMap.remove(mergeTypeAndTag(type, tag));
- }
-
- public List listTemplatesByType(String type, String tag) {
- return templateResourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
- }
-
- public Map> listAllTemplates() {
- return templateResourceTagMap;
- }
-
}
diff --git a/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java b/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java
index 424743e..00951f8 100644
--- a/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java
+++ b/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java
@@ -38,6 +38,8 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
public static final String TOLERANT_KEY = "tolerant";
/** 类型 key, 标识是哪张类型的验证码. */
public static final String TYPE_KEY = "type";
+ /** 点选类验证码验证时判断是否需要校验顺序. */
+ public static final String CLICK_IMAGE_CHECK_ORDER_KEY = "click_image_check_order";
/** 计算当前验证码用户滑动的百分比率 - 生成时的百分比率, 多个的话取均值. */
public static final String USER_CURRENT_PERCENTAGE_STD = "user_current_percentage_std";
public static final String USER_CURRENT_PERCENTAGE = "user_current_percentage";
@@ -145,6 +147,12 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
map.put(TOLERANT_KEY, tolerant);
}
}
+ // 新增是否判断顺序
+ if (imageCaptchaInfo.getData() != null && imageCaptchaInfo.getData().getData() != null) {
+ map.put(CLICK_IMAGE_CHECK_ORDER_KEY, imageCaptchaInfo.getData().getData().getOrDefault(CLICK_IMAGE_CHECK_ORDER_KEY, true));
+ } else {
+ map.put(CLICK_IMAGE_CHECK_ORDER_KEY, true);
+ }
// 添加点选验证数据
map.put(PERCENTAGE_KEY, sb.toString());
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
@@ -156,7 +164,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
@Override
public ApiResponse> valid(ImageCaptchaTrack imageCaptchaTrack, AnyMap imageCaptchaValidData) {
// 读容错值
- Float tolerant = imageCaptchaValidData.getFloat(TOLERANT_KEY, defaultTolerant);
+ Float tolerant = recalculateTolerant(imageCaptchaValidData.getFloat(TOLERANT_KEY, defaultTolerant), imageCaptchaTrack, imageCaptchaValidData);
// 读验证码类型
String type = imageCaptchaValidData.getString(TYPE_KEY, CaptchaTypeConstant.SLIDER);
// 验证前
@@ -176,15 +184,26 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
return ApiResponse.ofCheckError("没有解析到滑动轨迹");
}
// 验证
- ApiResponse> response;
boolean valid = doValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
return afterValid(valid, imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
}
+ /**
+ * 一个模板方法, 用于自定义处理容错值
+ *
+ * @param tolerant 容错值
+ * @param imageCaptchaTrack imageCaptchaTrack
+ * @param imageCaptchaValidData captchaValidData
+ * @return
+ */
+ public Float recalculateTolerant(Float tolerant, ImageCaptchaTrack imageCaptchaTrack, AnyMap imageCaptchaValidData) {
+ return tolerant;
+ }
+
/**
* 验证前
*
- * @param imageCaptchaTrack sliderCaptchaTrack
+ * @param imageCaptchaTrack imageCaptchaTrack
* @param captchaValidData captchaValidData
* @param tolerant tolerant
* @param type type
@@ -197,7 +216,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
/**
* 验证后
*
- * @param imageCaptchaTrack sliderCaptchaTrack
+ * @param imageCaptchaTrack imageCaptchaTrack
* @param captchaValidData captchaValidData
* @param tolerant tolerant
* @param type type
@@ -241,7 +260,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
/**
* 校验点选验证码
*
- * @param imageCaptchaTrack sliderCaptchaTrack
+ * @param imageCaptchaTrack imageCaptchaTrack
* @param imageCaptchaValidData imageCaptchaValidData
* @param tolerant tolerant
* @param type type
@@ -252,6 +271,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
Float tolerant,
String type) {
String validStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, null);
+ Object checkOrder = imageCaptchaValidData.getOrDefault(CLICK_IMAGE_CHECK_ORDER_KEY, true);
if (ObjectUtils.isEmpty(validStr)) {
return false;
}
@@ -271,17 +291,38 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
StringBuilder sb = new StringBuilder();
List percentages = new ArrayList<>();
for (int i = 0; i < splitArr.length; i++) {
- ImageCaptchaTrack.Track track = clickTrackList.get(i);
String posStr = splitArr[i];
String[] posArr = posStr.split(",");
float xPercentage = Float.parseFloat(posArr[0]);
float yPercentage = Float.parseFloat(posArr[1]);
-
- float calcXPercentage = calcPercentage(track.getX(), imageCaptchaTrack.getBgImageWidth());
- float calcYPercentage = calcPercentage(track.getY(), imageCaptchaTrack.getBgImageHeight());
- if (!checkPercentage(calcXPercentage, xPercentage, tolerant)
- || !checkPercentage(calcYPercentage, yPercentage, tolerant)) {
- return false;
+ float calcXPercentage = 0f;
+ float calcYPercentage = 0f;
+ if (Boolean.TRUE.equals(checkOrder)) {
+ ImageCaptchaTrack.Track track = clickTrackList.get(0);
+ calcXPercentage = calcPercentage(track.getX(), imageCaptchaTrack.getBgImageWidth());
+ calcYPercentage = calcPercentage(track.getY(), imageCaptchaTrack.getBgImageHeight());
+ if (!checkPercentage(calcXPercentage, xPercentage, tolerant)
+ || !checkPercentage(calcYPercentage, yPercentage, tolerant)) {
+ return false;
+ }
+ clickTrackList.remove(0);
+ } else {
+ boolean flag = false;
+ for (int a = 0; a < clickTrackList.size(); a++) {
+ ImageCaptchaTrack.Track track = clickTrackList.get(a);
+ calcXPercentage = calcPercentage(track.getX(), imageCaptchaTrack.getBgImageWidth());
+ calcYPercentage = calcPercentage(track.getY(), imageCaptchaTrack.getBgImageHeight());
+ if (checkPercentage(calcXPercentage, xPercentage, tolerant)
+ && checkPercentage(calcYPercentage, yPercentage, tolerant)) {
+ // 验证命中
+ clickTrackList.remove(a);
+ flag = true;
+ break;
+ }
+ }
+ if (!flag) {
+ return false;
+ }
}
if (i > 0) {
sb.append("|");
@@ -296,7 +337,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
/**
* 校验滑动验证码
*
- * @param imageCaptchaTrack sliderCaptchaTrack
+ * @param imageCaptchaTrack imageCaptchaTrack
* @param imageCaptchaValidData imageCaptchaValidData
* @param tolerant tolerant
* @param type type