mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
refactor(resource): 重构资源存储和管理逻辑
- 移除了 AbstractResourceStore 类 - 新增了 CrudResourceStore 接口,定义了 CRUD操作 - 修改了 DefaultImageCaptchaResourceManager,支持批量获取资源和模板 - 重构了 FontCache 类,改为实现 ResourceStore 接口 - 更新了相关应用类,使用新的资源管理逻辑
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>cloud.tianai.captcha</groupId>
|
<groupId>cloud.tianai.captcha</groupId>
|
||||||
<artifactId>tianai-captcha</artifactId>
|
<artifactId>tianai-captcha</artifactId>
|
||||||
<version>1.5.2</version>
|
<version>1.5.3</version>
|
||||||
|
|
||||||
<name>tianai-captcha</name>
|
<name>tianai-captcha</name>
|
||||||
<description>行为验证码</description>
|
<description>行为验证码</description>
|
||||||
|
|||||||
@@ -27,12 +27,17 @@
|
|||||||
> 注意: 如果你项目是使用的**Springboot**,
|
> 注意: 如果你项目是使用的**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验证码进行了封装,使其使用更加方便快捷
|
> 该工具对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
|
### 1. 导入xml
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -1,6 +1,5 @@
|
|||||||
package cloud.tianai.captcha.application;
|
package cloud.tianai.captcha.application;
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.vo.CaptchaResponse;
|
|
||||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
import cloud.tianai.captcha.cache.CacheStore;
|
||||||
import cloud.tianai.captcha.common.AnyMap;
|
import cloud.tianai.captcha.common.AnyMap;
|
||||||
@@ -78,21 +77,21 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha() {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha() {
|
||||||
// 生成滑块验证码
|
// 生成滑块验证码
|
||||||
return generateCaptcha(CaptchaTypeConstant.SLIDER);
|
return generateCaptcha(CaptchaTypeConstant.SLIDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(String type) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(String type) {
|
||||||
GenerateParam generateParam = new GenerateParam();
|
GenerateParam generateParam = new GenerateParam();
|
||||||
generateParam.setType(type);
|
generateParam.setType(type);
|
||||||
return generateCaptcha(generateParam);
|
return generateCaptcha(generateParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(GenerateParam param) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(GenerateParam param) {
|
||||||
CaptchaResponse<ImageCaptchaVO> captchaResponse = beforeGenerateCaptcha(param);
|
ApiResponse<ImageCaptchaVO> captchaResponse = beforeGenerateCaptcha(param);
|
||||||
if (captchaResponse != null) {
|
if (captchaResponse != null) {
|
||||||
return captchaResponse;
|
return captchaResponse;
|
||||||
}
|
}
|
||||||
@@ -103,12 +102,12 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(CaptchaImageType captchaImageType) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(CaptchaImageType captchaImageType) {
|
||||||
return generateCaptcha(CaptchaTypeConstant.SLIDER, captchaImageType);
|
return generateCaptcha(CaptchaTypeConstant.SLIDER, captchaImageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(String type, CaptchaImageType captchaImageType) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(String type, CaptchaImageType captchaImageType) {
|
||||||
GenerateParam param = new GenerateParam();
|
GenerateParam param = new GenerateParam();
|
||||||
if (CaptchaImageType.WEBP.equals(captchaImageType)) {
|
if (CaptchaImageType.WEBP.equals(captchaImageType)) {
|
||||||
param.setBackgroundFormatName("webp");
|
param.setBackgroundFormatName("webp");
|
||||||
@@ -122,14 +121,14 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CaptchaResponse<ImageCaptchaVO> convertToCaptchaResponse(ImageCaptchaInfo imageCaptchaInfo) {
|
public ApiResponse<ImageCaptchaVO> convertToCaptchaResponse(ImageCaptchaInfo imageCaptchaInfo) {
|
||||||
if (imageCaptchaInfo == null) {
|
if (imageCaptchaInfo == null) {
|
||||||
// 要是生成失败
|
// 要是生成失败
|
||||||
throw new ImageCaptchaException("生成验证码失败,验证码生成为空");
|
throw new ImageCaptchaException("生成验证码失败,验证码生成为空");
|
||||||
}
|
}
|
||||||
// 生成ID
|
// 生成ID
|
||||||
String id = generatorId(imageCaptchaInfo);
|
String id = generatorId(imageCaptchaInfo);
|
||||||
CaptchaResponse<ImageCaptchaVO> response = beforeGenerateImageCaptchaValidData(imageCaptchaInfo);
|
ApiResponse<ImageCaptchaVO> response = beforeGenerateImageCaptchaValidData(imageCaptchaInfo);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -151,7 +150,8 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
|||||||
verificationVO.setTemplateImageWidth(imageCaptchaInfo.getTemplateImageWidth());
|
verificationVO.setTemplateImageWidth(imageCaptchaInfo.getTemplateImageWidth());
|
||||||
verificationVO.setTemplateImageHeight(imageCaptchaInfo.getTemplateImageHeight());
|
verificationVO.setTemplateImageHeight(imageCaptchaInfo.getTemplateImageHeight());
|
||||||
verificationVO.setData(imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getViewData());
|
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<ImageCaptchaVO> captchaResponse) {
|
private void afterGenerateCaptcha(ImageCaptchaInfo imageCaptchaInfo, ApiResponse<ImageCaptchaVO> captchaResponse) {
|
||||||
captchaInterceptor.afterGenerateCaptcha(captchaInterceptor.createContext(), imageCaptchaInfo.getType(), imageCaptchaInfo, captchaResponse);
|
captchaInterceptor.afterGenerateCaptcha(captchaInterceptor.createContext(), imageCaptchaInfo.getType(), imageCaptchaInfo, captchaResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CaptchaResponse<ImageCaptchaVO> beforeGenerateCaptcha(GenerateParam param) {
|
private ApiResponse<ImageCaptchaVO> beforeGenerateCaptcha(GenerateParam param) {
|
||||||
return captchaInterceptor.beforeGenerateCaptcha(captchaInterceptor.createContext(), param.getType(), param);
|
return captchaInterceptor.beforeGenerateCaptcha(captchaInterceptor.createContext(), param.getType(), param);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CaptchaResponse<ImageCaptchaVO> beforeGenerateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
|
private ApiResponse<ImageCaptchaVO> beforeGenerateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
|
||||||
return captchaInterceptor.beforeGenerateImageCaptchaValidData(captchaInterceptor.createContext(), imageCaptchaInfo.getType(), imageCaptchaInfo);
|
return captchaInterceptor.beforeGenerateImageCaptchaValidData(captchaInterceptor.createContext(), imageCaptchaInfo.getType(), imageCaptchaInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cloud.tianai.captcha.application;
|
package cloud.tianai.captcha.application;
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.vo.CaptchaResponse;
|
|
||||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
import cloud.tianai.captcha.cache.CacheStore;
|
||||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||||
@@ -27,27 +26,27 @@ public class FilterImageCaptchaApplication implements ImageCaptchaApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha() {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha() {
|
||||||
return target.generateCaptcha();
|
return target.generateCaptcha();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(String type) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(String type) {
|
||||||
return target.generateCaptcha(type);
|
return target.generateCaptcha(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(CaptchaImageType captchaImageType) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(CaptchaImageType captchaImageType) {
|
||||||
return target.generateCaptcha(captchaImageType);
|
return target.generateCaptcha(captchaImageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(String type, CaptchaImageType captchaImageType) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(String type, CaptchaImageType captchaImageType) {
|
||||||
return target.generateCaptcha(type, captchaImageType);
|
return target.generateCaptcha(type, captchaImageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> generateCaptcha(GenerateParam param) {
|
public ApiResponse<ImageCaptchaVO> generateCaptcha(GenerateParam param) {
|
||||||
return target.generateCaptcha(param);
|
return target.generateCaptcha(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cloud.tianai.captcha.application;
|
package cloud.tianai.captcha.application;
|
||||||
|
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.vo.CaptchaResponse;
|
|
||||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
import cloud.tianai.captcha.cache.CacheStore;
|
||||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||||
@@ -25,7 +24,7 @@ public interface ImageCaptchaApplication {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
CaptchaResponse<ImageCaptchaVO> generateCaptcha();
|
ApiResponse<ImageCaptchaVO> generateCaptcha();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成滑块验证码
|
* 生成滑块验证码
|
||||||
@@ -33,7 +32,7 @@ public interface ImageCaptchaApplication {
|
|||||||
* @param type type类型
|
* @param type type类型
|
||||||
* @return CaptchaResponse<SliderCaptchaVO>
|
* @return CaptchaResponse<SliderCaptchaVO>
|
||||||
*/
|
*/
|
||||||
CaptchaResponse<ImageCaptchaVO> generateCaptcha(String type);
|
ApiResponse<ImageCaptchaVO> generateCaptcha(String type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成滑块验证码
|
* 生成滑块验证码
|
||||||
@@ -41,7 +40,7 @@ public interface ImageCaptchaApplication {
|
|||||||
* @param captchaImageType 要生成webp还是jpg类型的图片
|
* @param captchaImageType 要生成webp还是jpg类型的图片
|
||||||
* @return CaptchaResponse<SliderCaptchaVO>
|
* @return CaptchaResponse<SliderCaptchaVO>
|
||||||
*/
|
*/
|
||||||
CaptchaResponse<ImageCaptchaVO> generateCaptcha(CaptchaImageType captchaImageType);
|
ApiResponse<ImageCaptchaVO> generateCaptcha(CaptchaImageType captchaImageType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成验证码
|
* 生成验证码
|
||||||
@@ -50,7 +49,7 @@ public interface ImageCaptchaApplication {
|
|||||||
* @param captchaImageType CaptchaImageType
|
* @param captchaImageType CaptchaImageType
|
||||||
* @return CaptchaResponse<ImageCaptchaVO>
|
* @return CaptchaResponse<ImageCaptchaVO>
|
||||||
*/
|
*/
|
||||||
CaptchaResponse<ImageCaptchaVO> generateCaptcha(String type, CaptchaImageType captchaImageType);
|
ApiResponse<ImageCaptchaVO> generateCaptcha(String type, CaptchaImageType captchaImageType);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,7 +58,7 @@ public interface ImageCaptchaApplication {
|
|||||||
* @param param param
|
* @param param param
|
||||||
* @return CaptchaResponse<SliderCaptchaVO>
|
* @return CaptchaResponse<SliderCaptchaVO>
|
||||||
*/
|
*/
|
||||||
CaptchaResponse<ImageCaptchaVO> generateCaptcha(GenerateParam param);
|
ApiResponse<ImageCaptchaVO> generateCaptcha(GenerateParam param);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 匹配
|
* 匹配
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ import cloud.tianai.captcha.generator.ImageTransform;
|
|||||||
import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
|
import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
|
||||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||||
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
||||||
import cloud.tianai.captcha.resource.DefaultBuiltInResources;
|
import cloud.tianai.captcha.resource.*;
|
||||||
import cloud.tianai.captcha.resource.FontCache;
|
|
||||||
import cloud.tianai.captcha.resource.ResourceProviders;
|
|
||||||
import cloud.tianai.captcha.resource.ResourceStore;
|
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
|
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
|
||||||
@@ -49,8 +46,8 @@ public class TACBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
||||||
DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultPathPrefix);
|
// DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultPathPrefix);
|
||||||
defaultBuiltInResources.addDefaultTemplate(resourceStore);
|
// defaultBuiltInResources.addDefaultTemplate(resourceStore);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,12 +112,16 @@ public class TACBuilder {
|
|||||||
|
|
||||||
|
|
||||||
public TACBuilder addResource(String captchaType, Resource imageResource) {
|
public TACBuilder addResource(String captchaType, Resource imageResource) {
|
||||||
this.resourceStore.addResource(captchaType, imageResource);
|
if (resourceStore instanceof CrudResourceStore) {
|
||||||
|
((CrudResourceStore) resourceStore).addResource(captchaType, imageResource);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder addTemplate(String captchaType, ResourceMap resourceMap) {
|
public TACBuilder addTemplate(String captchaType, ResourceMap resourceMap) {
|
||||||
this.resourceStore.addTemplate(captchaType, resourceMap);
|
if (resourceStore instanceof CrudResourceStore) {
|
||||||
|
((CrudResourceStore) resourceStore).addTemplate(captchaType, resourceMap);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import java.io.Serializable;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ImageCaptchaVO implements Serializable {
|
public class ImageCaptchaVO implements Serializable {
|
||||||
|
|
||||||
|
/** ID.*/
|
||||||
|
private String id;
|
||||||
/** 验证码类型.*/
|
/** 验证码类型.*/
|
||||||
private String type;
|
private String type;
|
||||||
/** 背景图.*/
|
/** 背景图.*/
|
||||||
|
|||||||
@@ -3,13 +3,18 @@ package cloud.tianai.captcha.common.constant;
|
|||||||
public interface CommonConstant {
|
public interface CommonConstant {
|
||||||
|
|
||||||
String DEFAULT_TAG = "default";
|
String DEFAULT_TAG = "default";
|
||||||
|
|
||||||
|
|
||||||
/** 图标点选资源存储类型. */
|
/** 图标点选资源存储类型. */
|
||||||
String IMAGE_CLICK_ICON = "ICON";
|
String IMAGE_ICON = "ICON";
|
||||||
/** 蜂窝点选.*/
|
/** 蜂窝点选.*/
|
||||||
String HONEYCOMB_CLICK_ICON = "HONEYCOMB_ICON";
|
String HONEYCOMB_CLICK_ICON = "HONEYCOMB_ICON";
|
||||||
/** 刮刮卡图标. */
|
/** 刮刮卡图标. */
|
||||||
String SCRAPE_ICON = "SCRAPE_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资源文件路径.
|
* 默认的resource资源文件路径.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import java.io.Serializable;
|
|||||||
/**
|
/**
|
||||||
* @Author: 天爱有情
|
* @Author: 天爱有情
|
||||||
* @date 2023/4/20 9:53
|
* @date 2023/4/20 9:53
|
||||||
* @Description 可能是最好用的API统一返回格式类
|
* @Description API统一返回格式类
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package cloud.tianai.captcha.generator.common.model.dto;
|
|||||||
|
|
||||||
import cloud.tianai.captcha.common.AnyMap;
|
import cloud.tianai.captcha.common.AnyMap;
|
||||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||||
import lombok.*;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: 天爱有情
|
* @Author: 天爱有情
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
|||||||
|
|
||||||
protected Map<String, ImageCaptchaGenerator> imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4);
|
protected Map<String, ImageCaptchaGenerator> imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4);
|
||||||
protected Map<String, ImageCaptchaGeneratorProvider> imageCaptchaGeneratorProviderMap = new HashMap<>(4);
|
protected Map<String, ImageCaptchaGeneratorProvider> imageCaptchaGeneratorProviderMap = new HashMap<>(4);
|
||||||
|
// 点选类验证码字体
|
||||||
|
// @Setter
|
||||||
|
// @Getter
|
||||||
|
// protected List<FontWrapper> fontWrappers;
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
private String defaultCaptcha = SLIDER;
|
private String defaultCaptcha = SLIDER;
|
||||||
|
|||||||
+2
-10
@@ -88,14 +88,11 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(activeTemplate.getWidth(), background.getHeight());
|
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(activeTemplate.getWidth(), background.getHeight());
|
||||||
CaptchaImageUtils.overlayImage(matrixTemplate, cutImage, 0, randomY);
|
CaptchaImageUtils.overlayImage(matrixTemplate, cutImage, 0, randomY);
|
||||||
|
|
||||||
XandY xandY = new XandY();
|
|
||||||
xandY.x = randomX;
|
|
||||||
xandY.y = randomY;
|
|
||||||
captchaExchange.setBackgroundImage(background);
|
captchaExchange.setBackgroundImage(background);
|
||||||
captchaExchange.setTemplateImage(matrixTemplate);
|
captchaExchange.setTemplateImage(matrixTemplate);
|
||||||
captchaExchange.setTemplateResource(templateResource);
|
captchaExchange.setTemplateResource(templateResource);
|
||||||
captchaExchange.setResourceImage(resourceImage);
|
captchaExchange.setResourceImage(resourceImage);
|
||||||
captchaExchange.setTransferData(xandY);
|
captchaExchange.setTransferData(new Point(randomX,randomY));
|
||||||
// 后处理
|
// 后处理
|
||||||
// applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
|
// applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
|
||||||
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, captchaExchange);
|
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, captchaExchange);
|
||||||
@@ -125,11 +122,6 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class XandY {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
|
public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
|
||||||
@@ -139,7 +131,7 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
Resource resourceImage = captchaExchange.getResourceImage();
|
Resource resourceImage = captchaExchange.getResourceImage();
|
||||||
ResourceMap templateResource = captchaExchange.getTemplateResource();
|
ResourceMap templateResource = captchaExchange.getTemplateResource();
|
||||||
CustomData customData = captchaExchange.getCustomData();
|
CustomData customData = captchaExchange.getCustomData();
|
||||||
XandY data = (XandY) captchaExchange.getTransferData();
|
Point data = (Point) captchaExchange.getTransferData();
|
||||||
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData);
|
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData);
|
||||||
|
|
||||||
SliderImageCaptchaInfo imageCaptchaInfo = SliderImageCaptchaInfo.of(data.x, data.y,
|
SliderImageCaptchaInfo imageCaptchaInfo = SliderImageCaptchaInfo.of(data.x, data.y,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cloud.tianai.captcha.interceptor;
|
package cloud.tianai.captcha.interceptor;
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.vo.CaptchaResponse;
|
|
||||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
||||||
import cloud.tianai.captcha.common.AnyMap;
|
import cloud.tianai.captcha.common.AnyMap;
|
||||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||||
@@ -41,18 +40,18 @@ public interface CaptchaInterceptor {
|
|||||||
return new Context(getName(), null, -1, 1, EmptyCaptchaInterceptor.INSTANCE);
|
return new Context(getName(), null, -1, 1, EmptyCaptchaInterceptor.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
default CaptchaResponse<ImageCaptchaVO> beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
|
default ApiResponse<ImageCaptchaVO> beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
default CaptchaResponse<ImageCaptchaVO> beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
|
default ApiResponse<ImageCaptchaVO> beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void afterGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, AnyMap validData) {
|
default void afterGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, AnyMap validData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, CaptchaResponse<ImageCaptchaVO> captchaResponse) {
|
default void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, ApiResponse<ImageCaptchaVO> captchaResponse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default ApiResponse<?> beforeValid(Context context, String type, MatchParam matchParam, AnyMap validData) {
|
default ApiResponse<?> beforeValid(Context context, String type, MatchParam matchParam, AnyMap validData) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cloud.tianai.captcha.interceptor;
|
package cloud.tianai.captcha.interceptor;
|
||||||
|
|
||||||
import cloud.tianai.captcha.application.vo.CaptchaResponse;
|
|
||||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
||||||
import cloud.tianai.captcha.common.AnyMap;
|
import cloud.tianai.captcha.common.AnyMap;
|
||||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||||
@@ -66,9 +65,9 @@ public class CaptchaInterceptorGroup implements CaptchaInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
|
public ApiResponse<ImageCaptchaVO> beforeGenerateCaptcha(Context context, String type, GenerateParam param) {
|
||||||
context = createContextIfNecessary(context);
|
context = createContextIfNecessary(context);
|
||||||
CaptchaResponse<ImageCaptchaVO> captchaResponse = null;
|
ApiResponse<ImageCaptchaVO> captchaResponse = null;
|
||||||
while (context.next() < context.getCount()) {
|
while (context.next() < context.getCount()) {
|
||||||
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
|
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
|
||||||
captchaResponse = interceptor.beforeGenerateCaptcha(context, type, param);
|
captchaResponse = interceptor.beforeGenerateCaptcha(context, type, param);
|
||||||
@@ -78,7 +77,7 @@ public class CaptchaInterceptorGroup implements CaptchaInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, CaptchaResponse<ImageCaptchaVO> captchaResponse) {
|
public void afterGenerateCaptcha(Context context, String type, ImageCaptchaInfo imageCaptchaInfo, ApiResponse<ImageCaptchaVO> captchaResponse) {
|
||||||
context = createContextIfNecessary(context);
|
context = createContextIfNecessary(context);
|
||||||
while (context.next() < context.getCount()) {
|
while (context.next() < context.getCount()) {
|
||||||
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
|
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
|
||||||
@@ -111,9 +110,9 @@ public class CaptchaInterceptorGroup implements CaptchaInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResponse<ImageCaptchaVO> beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
|
public ApiResponse<ImageCaptchaVO> beforeGenerateImageCaptchaValidData(Context context, String type, ImageCaptchaInfo imageCaptchaInfo) {
|
||||||
context = createContextIfNecessary(context);
|
context = createContextIfNecessary(context);
|
||||||
CaptchaResponse<ImageCaptchaVO> captchaResponse = null;
|
ApiResponse<ImageCaptchaVO> captchaResponse = null;
|
||||||
while (context.next() < context.getCount()) {
|
while (context.next() < context.getCount()) {
|
||||||
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
|
CaptchaInterceptor interceptor = validators.get(context.getCurrent());
|
||||||
captchaResponse = interceptor.beforeGenerateImageCaptchaValidData(context, type, imageCaptchaInfo);
|
captchaResponse = interceptor.beforeGenerateImageCaptchaValidData(context, type, imageCaptchaInfo);
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import java.util.List;
|
|||||||
* @Description BasicCaptchaTrackValidator
|
* @Description BasicCaptchaTrackValidator
|
||||||
*/
|
*/
|
||||||
public class BasicTrackCaptchaInterceptor implements CaptchaInterceptor {
|
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
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|||||||
@@ -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<ResourceListener> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<Resource>
|
||||||
|
*/
|
||||||
|
List<Resource> listResourcesByTypeAndTag(String type, String tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某个模板列表
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param tag 资源标签(可为空)
|
||||||
|
* @return List<ResourceMap>
|
||||||
|
*/
|
||||||
|
List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有内置模板
|
||||||
|
*/
|
||||||
|
void clearAllTemplates();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有内置资源
|
||||||
|
*/
|
||||||
|
void clearAllResources();
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ public class DefaultBuiltInResources {
|
|||||||
|
|
||||||
public static final String PATH_PREFIX = "classpath:META-INF/cut-image/template";
|
public static final String PATH_PREFIX = "classpath:META-INF/cut-image/template";
|
||||||
|
|
||||||
private static Map<String, Consumer<ResourceStore>> defaultTemplateResource = new HashMap<>(8);
|
private static Map<String, Consumer<CrudResourceStore>> defaultTemplateResource = new HashMap<>(8);
|
||||||
|
|
||||||
|
|
||||||
public DefaultBuiltInResources(String defaultPathPrefix) {
|
public DefaultBuiltInResources(String defaultPathPrefix) {
|
||||||
@@ -75,17 +75,21 @@ public class DefaultBuiltInResources {
|
|||||||
|
|
||||||
|
|
||||||
public void addDefaultTemplate(String type, ResourceStore resourceStore) {
|
public void addDefaultTemplate(String type, ResourceStore resourceStore) {
|
||||||
Consumer<ResourceStore> resourceStoreConsumer = defaultTemplateResource.get(type);
|
if (resourceStore instanceof CrudResourceStore) {
|
||||||
if (resourceStoreConsumer == null) {
|
Consumer<CrudResourceStore> resourceStoreConsumer = defaultTemplateResource.get(type);
|
||||||
return;
|
if (resourceStoreConsumer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resourceStoreConsumer.accept((CrudResourceStore) resourceStore);
|
||||||
}
|
}
|
||||||
resourceStoreConsumer.accept(resourceStore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDefaultTemplate(ResourceStore resourceStore) {
|
public void addDefaultTemplate(ResourceStore resourceStore) {
|
||||||
defaultTemplateResource.forEach((type, consumer) -> {
|
if (resourceStore instanceof CrudResourceStore) {
|
||||||
consumer.accept(resourceStore);
|
defaultTemplateResource.forEach((type, consumer) -> {
|
||||||
});
|
consumer.accept((CrudResourceStore) resourceStore);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package cloud.tianai.captcha.resource;
|
|||||||
|
|
||||||
import cloud.tianai.captcha.generator.common.FontWrapper;
|
import cloud.tianai.captcha.generator.common.FontWrapper;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
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.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -20,7 +20,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* @Description 一个用于统一缓存字体文件的对象
|
* @Description 一个用于统一缓存字体文件的对象
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FontCache implements ResourceListener {
|
public class FontCache implements ResourceStore {
|
||||||
|
|
||||||
|
|
||||||
public static final String FONT_TYPE = "font";
|
public static final String FONT_TYPE = "font";
|
||||||
@@ -31,19 +31,20 @@ public class FontCache implements ResourceListener {
|
|||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
private int fontSize = 70;
|
private int fontSize = 70;
|
||||||
public static FontCache getInstance() {
|
|
||||||
return INSTANCE.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FontCache() {
|
|
||||||
|
|
||||||
|
public FontCache(ResourceStore resourceStore) {
|
||||||
|
this.resourceStore = resourceStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
|
public void init(ImageCaptchaResourceManager resourceManager) {
|
||||||
this.resourceStore = resourceStore;
|
resourceStore.init(resourceManager);
|
||||||
this.resourceManager = resourceManager;
|
this.resourceManager = resourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public FontWrapper getFont(Resource resource) {
|
public FontWrapper getFont(Resource resource) {
|
||||||
try (InputStream stream = resourceManager.getResourceInputStream(resource)) {
|
try (InputStream stream = resourceManager.getResourceInputStream(resource)) {
|
||||||
Font font = Font.createFont(0, stream);
|
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
|
@Override
|
||||||
public void onAddResource(String type, Resource resource) {
|
public List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
|
||||||
|
List<Resource> resources = resourceStore.randomGetResourceByTypeAndTag(type, tag, quantity);
|
||||||
|
// 字体增强
|
||||||
if (FONT_TYPE.equalsIgnoreCase(type)) {
|
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
|
@Override
|
||||||
public void onDeleteResource(String type, Resource resource) {
|
public List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
|
||||||
if (FONT_TYPE.equalsIgnoreCase(type)) {
|
return resourceStore.randomGetTemplateByTypeAndTag(type, tag, quantity);
|
||||||
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<Resource> 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,27 @@ public interface ImageCaptchaResourceManager {
|
|||||||
*/
|
*/
|
||||||
Resource randomGetResource(String type, String tag);
|
Resource randomGetResource(String type, String tag);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机获取某个模板
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param tag 二级过滤,可以为空
|
||||||
|
* @param quantity 一次性获取的数量
|
||||||
|
* @return Map<String, Resource>
|
||||||
|
*/
|
||||||
|
List<ResourceMap> randomGetTemplate(String type, String tag, Integer quantity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机获取某个资源对象
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param tag 二级过滤,可以为空
|
||||||
|
* @param quantity 一次性获取的数量
|
||||||
|
* @return Resource
|
||||||
|
*/
|
||||||
|
List<Resource> randomGetResource(String type, String tag, Integer quantity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取真正的资源流通过资源对象
|
* 获取真正的资源流通过资源对象
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -13,65 +13,6 @@ import java.util.List;
|
|||||||
public interface ResourceStore {
|
public interface ResourceStore {
|
||||||
|
|
||||||
void init(ImageCaptchaResourceManager resourceManager);
|
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<Resource>
|
|
||||||
*/
|
|
||||||
List<Resource> listResourcesByTypeAndTag(String type, String tag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取某个模板列表
|
|
||||||
*
|
|
||||||
* @param type 验证码类型
|
|
||||||
* @param tag 资源标签(可为空)
|
|
||||||
* @return List<ResourceMap>
|
|
||||||
*/
|
|
||||||
List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 随机获取某个资源
|
* 随机获取某个资源
|
||||||
@@ -79,7 +20,7 @@ public interface ResourceStore {
|
|||||||
* @param type type
|
* @param type type
|
||||||
* @return Resource
|
* @return Resource
|
||||||
*/
|
*/
|
||||||
Resource randomGetResourceByTypeAndTag(String type, String tag);
|
List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 随机获取某个模板通过type
|
* 随机获取某个模板通过type
|
||||||
@@ -87,16 +28,5 @@ public interface ResourceStore {
|
|||||||
* @param type type
|
* @param type type
|
||||||
* @return Map<String, Resource>
|
* @return Map<String, Resource>
|
||||||
*/
|
*/
|
||||||
ResourceMap randomGetTemplateByTypeAndTag(String type, String tag);
|
List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag,Integer quantity);
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除所有内置模板
|
|
||||||
*/
|
|
||||||
void clearAllTemplates();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除所有内置资源
|
|
||||||
*/
|
|
||||||
void clearAllResources();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-11
@@ -1,12 +1,15 @@
|
|||||||
package cloud.tianai.captcha.resource.impl;
|
package cloud.tianai.captcha.resource.impl;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||||
import cloud.tianai.captcha.resource.*;
|
import cloud.tianai.captcha.resource.*;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: 天爱有情
|
* @Author: 天爱有情
|
||||||
@@ -36,28 +39,43 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
|
|||||||
this.resourceStore = new LocalMemoryResourceStore();
|
this.resourceStore = new LocalMemoryResourceStore();
|
||||||
}
|
}
|
||||||
// 在这里临时加上字体缓存器
|
// 在这里临时加上字体缓存器
|
||||||
resourceStore.addListener(FontCache.getInstance());
|
resourceStore = new FontCache(resourceStore);
|
||||||
resourceStore.init(this);
|
resourceStore.init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceMap randomGetTemplate(String type, String tag) {
|
public ResourceMap randomGetTemplate(String type, String tag) {
|
||||||
ResourceMap resourceMap = resourceStore.randomGetTemplateByTypeAndTag(type, tag);
|
return randomGetTemplate(type, tag, 1).get(0);
|
||||||
if (resourceMap == null) {
|
|
||||||
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
|
|
||||||
}
|
|
||||||
return resourceMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource randomGetResource(String type, String tag) {
|
public Resource randomGetResource(String type, String tag) {
|
||||||
Resource resource = resourceStore.randomGetResourceByTypeAndTag(type, tag);
|
return randomGetResource(type, tag, 1).get(0);
|
||||||
if (resource == null) {
|
|
||||||
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
|
|
||||||
}
|
|
||||||
return resource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ResourceMap> randomGetTemplate(String type, String tag, Integer quantity) {
|
||||||
|
List<ResourceMap> 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<Resource> randomGetResource(String type, String tag, Integer quantity) {
|
||||||
|
List<Resource> 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
|
@Override
|
||||||
public InputStream getResourceInputStream(Resource resource) {
|
public InputStream getResourceInputStream(Resource resource) {
|
||||||
return resourceProviders.getResourceInputStream(resource);
|
return resourceProviders.getResourceInputStream(resource);
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package cloud.tianai.captcha.resource.impl;
|
|||||||
import cloud.tianai.captcha.common.constant.CommonConstant;
|
import cloud.tianai.captcha.common.constant.CommonConstant;
|
||||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||||
import cloud.tianai.captcha.common.util.ObjectUtils;
|
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.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
|
|
||||||
@@ -15,43 +16,52 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
* @date 2021/8/7 15:43
|
* @date 2021/8/7 15:43
|
||||||
* @Description 默认的资源存储
|
* @Description 默认的资源存储
|
||||||
*/
|
*/
|
||||||
public class LocalMemoryResourceStore extends AbstractResourceStore {
|
public class LocalMemoryResourceStore implements CrudResourceStore {
|
||||||
private static final String TYPE_TAG_SPLIT_FLAG = "|";
|
|
||||||
|
|
||||||
/** 用于检索 type和tag. */
|
/** 用于检索 type和tag. */
|
||||||
private Map<String, List<ResourceMap>> templateResourceTagMap = new HashMap<>(2);
|
private final Map<String, Map<String, List<ResourceMap>>> templateResourceTagMap = new HashMap<>(2);
|
||||||
private Map<String, List<Resource>> resourceTagMap = new HashMap<>(2);
|
private final Map<String, Map<String, List<Resource>>> resourceTagMap = new HashMap<>(2);
|
||||||
|
|
||||||
|
|
||||||
|
private void ensureTypeTagMapExists(Map<String, Map<String, List<Resource>>> map, String type, String tag) {
|
||||||
|
map.computeIfAbsent(type, k -> new HashMap<>())
|
||||||
|
.computeIfAbsent(tag, k -> new ArrayList<>(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureTypeTagMapExistsForTemplate(Map<String, Map<String, List<ResourceMap>>> map, String type, String tag) {
|
||||||
|
map.computeIfAbsent(type, k -> new HashMap<>())
|
||||||
|
.computeIfAbsent(tag, k -> new ArrayList<>(2));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doAddResource(String type, Resource resource) {
|
public void addResource(String type, Resource resource) {
|
||||||
if (ObjectUtils.isEmpty(resource.getTag())) {
|
if (ObjectUtils.isEmpty(resource.getTag())) {
|
||||||
resource.setTag(CommonConstant.DEFAULT_TAG);
|
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
|
@Override
|
||||||
public void doAddTemplate(String type, ResourceMap template) {
|
public void addTemplate(String type, ResourceMap template) {
|
||||||
if (ObjectUtils.isEmpty(template.getTag())) {
|
if (ObjectUtils.isEmpty(template.getTag())) {
|
||||||
template.setTag(CommonConstant.DEFAULT_TAG);
|
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
|
@Override
|
||||||
public Resource doDeleteResource(String type, String id) {
|
public Resource deleteResource(String type, String id) {
|
||||||
for (Map.Entry<String, List<Resource>> entry : resourceTagMap.entrySet()) {
|
Map<String, List<Resource>> tagMap = resourceTagMap.get(type);
|
||||||
String k = entry.getKey();
|
if (tagMap == null) return null;
|
||||||
List<Resource> v = entry.getValue();
|
|
||||||
String splitType = splitTypeTag(k)[0];
|
for (List<Resource> resources : tagMap.values()) {
|
||||||
if (splitType.equals(type)) {
|
Iterator<Resource> iterator = resources.iterator();
|
||||||
Iterator<Resource> iterator = v.iterator();
|
while (iterator.hasNext()) {
|
||||||
while (iterator.hasNext()) {
|
Resource res = iterator.next();
|
||||||
Resource next = iterator.next();
|
if (res.getId().equals(id)) {
|
||||||
if (next.getId().equals(id)) {
|
iterator.remove();
|
||||||
iterator.remove();
|
return res;
|
||||||
return next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,19 +69,17 @@ public class LocalMemoryResourceStore extends AbstractResourceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceMap doDeleteTemplate(String type, String id) {
|
public ResourceMap deleteTemplate(String type, String id) {
|
||||||
for (Map.Entry<String, List<ResourceMap>> entry : templateResourceTagMap.entrySet()) {
|
Map<String, List<ResourceMap>> tagMap = templateResourceTagMap.get(type);
|
||||||
String k = entry.getKey();
|
if (tagMap == null) return null;
|
||||||
List<ResourceMap> v = entry.getValue();
|
|
||||||
String splitType = splitTypeTag(k)[0];
|
for (List<ResourceMap> templates : tagMap.values()) {
|
||||||
if (splitType.equals(type)) {
|
Iterator<ResourceMap> iterator = templates.iterator();
|
||||||
Iterator<ResourceMap> iterator = v.iterator();
|
while (iterator.hasNext()) {
|
||||||
while (iterator.hasNext()) {
|
ResourceMap temp = iterator.next();
|
||||||
ResourceMap next = iterator.next();
|
if (temp.getId().equals(id)) {
|
||||||
if (next.getId().equals(id)) {
|
iterator.remove();
|
||||||
iterator.remove();
|
return temp;
|
||||||
return next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,135 +89,97 @@ public class LocalMemoryResourceStore extends AbstractResourceStore {
|
|||||||
@Override
|
@Override
|
||||||
public List<Resource> listResourcesByTypeAndTag(String type, String tag) {
|
public List<Resource> listResourcesByTypeAndTag(String type, String tag) {
|
||||||
if (!ObjectUtils.isEmpty(tag)) {
|
if (!ObjectUtils.isEmpty(tag)) {
|
||||||
return resourceTagMap.get(mergeTypeAndTag(type, tag));
|
Map<String, List<Resource>> tagMap = resourceTagMap.get(type);
|
||||||
|
return tagMap == null ? Collections.emptyList() : tagMap.getOrDefault(tag, Collections.emptyList());
|
||||||
}
|
}
|
||||||
List<Resource> resourceList = new ArrayList<>();
|
List<Resource> result = new ArrayList<>();
|
||||||
resourceTagMap.forEach((k, v) -> {
|
Map<String, List<Resource>> tagMap = resourceTagMap.get(type);
|
||||||
String splitType = splitTypeTag(k)[0];
|
if (tagMap != null) {
|
||||||
if (splitType.equals(type)) {
|
for (List<Resource> list : tagMap.values()) {
|
||||||
resourceList.addAll(v);
|
result.addAll(list);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return resourceList;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag) {
|
public List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag) {
|
||||||
if (!ObjectUtils.isEmpty(tag)) {
|
if (!ObjectUtils.isEmpty(tag)) {
|
||||||
return templateResourceTagMap.get(mergeTypeAndTag(type, tag));
|
Map<String, List<ResourceMap>> tagMap = templateResourceTagMap.get(type);
|
||||||
|
return tagMap == null ? Collections.emptyList() : tagMap.getOrDefault(tag, Collections.emptyList());
|
||||||
}
|
}
|
||||||
List<ResourceMap> resourceMapList = new ArrayList<>();
|
List<ResourceMap> result = new ArrayList<>();
|
||||||
templateResourceTagMap.forEach((k, v) -> {
|
Map<String, List<ResourceMap>> tagMap = templateResourceTagMap.get(type);
|
||||||
String splitType = splitTypeTag(k)[0];
|
if (tagMap != null) {
|
||||||
if (splitType.equals(type)) {
|
for (List<ResourceMap> list : tagMap.values()) {
|
||||||
resourceMapList.addAll(v);
|
result.addAll(list);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return resourceMapList;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource doRandomGetResourceByTypeAndTag(String type, String tag) {
|
public void init(ImageCaptchaResourceManager resourceManager) {
|
||||||
List<Resource> resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
|
||||||
|
List<Resource> resources = listResourcesByTypeAndTag(type, tag);
|
||||||
if (CollectionUtils.isEmpty(resources)) {
|
if (CollectionUtils.isEmpty(resources)) {
|
||||||
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
|
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
|
||||||
}
|
}
|
||||||
if (resources.size() == 1) {
|
int size = resources.size();
|
||||||
return resources.get(0);
|
if (quantity > size) {
|
||||||
|
throw new IllegalArgumentException("请求的资源数量超过可用资源总数");
|
||||||
}
|
}
|
||||||
int randomIndex = ThreadLocalRandom.current().nextInt(resources.size());
|
|
||||||
try {
|
Set<Integer> indexes = new HashSet<>(quantity);
|
||||||
return resources.get(randomIndex);
|
while (indexes.size() < quantity) {
|
||||||
} catch (IndexOutOfBoundsException e) {
|
indexes.add(ThreadLocalRandom.current().nextInt(size));
|
||||||
try {
|
|
||||||
Thread.sleep(0);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return doRandomGetResourceByTypeAndTag(type, tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Resource> result = new ArrayList<>(quantity);
|
||||||
|
for (int index : indexes) {
|
||||||
|
result.add(resources.get(index));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceMap doRandomGetTemplateByTypeAndTag(String type, String tag) {
|
public List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
|
||||||
List<ResourceMap> templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
|
List<ResourceMap> templates = listTemplatesByTypeAndTag(type, tag);
|
||||||
if (CollectionUtils.isEmpty(templateList)) {
|
if (CollectionUtils.isEmpty(templates)) {
|
||||||
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
|
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
|
||||||
}
|
}
|
||||||
if (templateList.size() == 1) {
|
int size = templates.size();
|
||||||
return templateList.get(0);
|
if (quantity > size) {
|
||||||
|
throw new IllegalArgumentException("请求的模板数量超过可用模板总数");
|
||||||
}
|
}
|
||||||
int randomIndex = ThreadLocalRandom.current().nextInt(templateList.size());
|
|
||||||
try {
|
Set<Integer> indexes = new HashSet<>(quantity);
|
||||||
return templateList.get(randomIndex);
|
while (indexes.size() < quantity) {
|
||||||
} catch (IndexOutOfBoundsException e) {
|
indexes.add(ThreadLocalRandom.current().nextInt(size));
|
||||||
try {
|
|
||||||
Thread.sleep(0);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return doRandomGetTemplateByTypeAndTag(type, tag);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public String mergeTypeAndTag(String type, String tag) {
|
List<ResourceMap> result = new ArrayList<>(quantity);
|
||||||
if (tag == null) {
|
for (int index : indexes) {
|
||||||
tag = CommonConstant.DEFAULT_TAG;
|
result.add(templates.get(index));
|
||||||
}
|
}
|
||||||
return type + TYPE_TAG_SPLIT_FLAG + tag;
|
return result;
|
||||||
}
|
|
||||||
|
|
||||||
public String[] splitTypeTag(String k) {
|
|
||||||
return k.split("\\" + TYPE_TAG_SPLIT_FLAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void clearResources(String type, String tag) {
|
|
||||||
resourceTagMap.remove(mergeTypeAndTag(type, tag));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doClearAllResources() {
|
public void clearAllResources() {
|
||||||
resourceTagMap.clear();
|
resourceTagMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, List<Resource>> listAllResources() {
|
|
||||||
return resourceTagMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Resource> listResourcesByType(String type, String tag) {
|
|
||||||
return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAllResourceCount() {
|
|
||||||
int count = 0;
|
|
||||||
for (List<Resource> 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
|
@Override
|
||||||
public void doClearAllTemplates() {
|
public void clearAllTemplates() {
|
||||||
templateResourceTagMap.clear();
|
templateResourceTagMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearTemplates(String type, String tag) {
|
|
||||||
templateResourceTagMap.remove(mergeTypeAndTag(type, tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ResourceMap> listTemplatesByType(String type, String tag) {
|
|
||||||
return templateResourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, List<ResourceMap>> listAllTemplates() {
|
|
||||||
return templateResourceTagMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-13
@@ -38,6 +38,8 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
public static final String TOLERANT_KEY = "tolerant";
|
public static final String TOLERANT_KEY = "tolerant";
|
||||||
/** 类型 key, 标识是哪张类型的验证码. */
|
/** 类型 key, 标识是哪张类型的验证码. */
|
||||||
public static final String TYPE_KEY = "type";
|
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_STD = "user_current_percentage_std";
|
||||||
public static final String USER_CURRENT_PERCENTAGE = "user_current_percentage";
|
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);
|
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());
|
map.put(PERCENTAGE_KEY, sb.toString());
|
||||||
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
|
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
|
||||||
@@ -156,7 +164,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
@Override
|
@Override
|
||||||
public ApiResponse<?> valid(ImageCaptchaTrack imageCaptchaTrack, AnyMap imageCaptchaValidData) {
|
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);
|
String type = imageCaptchaValidData.getString(TYPE_KEY, CaptchaTypeConstant.SLIDER);
|
||||||
// 验证前
|
// 验证前
|
||||||
@@ -176,15 +184,26 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
return ApiResponse.ofCheckError("没有解析到滑动轨迹");
|
return ApiResponse.ofCheckError("没有解析到滑动轨迹");
|
||||||
}
|
}
|
||||||
// 验证
|
// 验证
|
||||||
ApiResponse<?> response;
|
|
||||||
boolean valid = doValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
boolean valid = doValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||||
return afterValid(valid, 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 captchaValidData captchaValidData
|
||||||
* @param tolerant tolerant
|
* @param tolerant tolerant
|
||||||
* @param type type
|
* @param type type
|
||||||
@@ -197,7 +216,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
/**
|
/**
|
||||||
* 验证后
|
* 验证后
|
||||||
*
|
*
|
||||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
* @param imageCaptchaTrack imageCaptchaTrack
|
||||||
* @param captchaValidData captchaValidData
|
* @param captchaValidData captchaValidData
|
||||||
* @param tolerant tolerant
|
* @param tolerant tolerant
|
||||||
* @param type type
|
* @param type type
|
||||||
@@ -241,7 +260,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
/**
|
/**
|
||||||
* 校验点选验证码
|
* 校验点选验证码
|
||||||
*
|
*
|
||||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
* @param imageCaptchaTrack imageCaptchaTrack
|
||||||
* @param imageCaptchaValidData imageCaptchaValidData
|
* @param imageCaptchaValidData imageCaptchaValidData
|
||||||
* @param tolerant tolerant
|
* @param tolerant tolerant
|
||||||
* @param type type
|
* @param type type
|
||||||
@@ -252,6 +271,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
Float tolerant,
|
Float tolerant,
|
||||||
String type) {
|
String type) {
|
||||||
String validStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, null);
|
String validStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, null);
|
||||||
|
Object checkOrder = imageCaptchaValidData.getOrDefault(CLICK_IMAGE_CHECK_ORDER_KEY, true);
|
||||||
if (ObjectUtils.isEmpty(validStr)) {
|
if (ObjectUtils.isEmpty(validStr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -271,17 +291,38 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
List<Double> percentages = new ArrayList<>();
|
List<Double> percentages = new ArrayList<>();
|
||||||
for (int i = 0; i < splitArr.length; i++) {
|
for (int i = 0; i < splitArr.length; i++) {
|
||||||
ImageCaptchaTrack.Track track = clickTrackList.get(i);
|
|
||||||
String posStr = splitArr[i];
|
String posStr = splitArr[i];
|
||||||
String[] posArr = posStr.split(",");
|
String[] posArr = posStr.split(",");
|
||||||
float xPercentage = Float.parseFloat(posArr[0]);
|
float xPercentage = Float.parseFloat(posArr[0]);
|
||||||
float yPercentage = Float.parseFloat(posArr[1]);
|
float yPercentage = Float.parseFloat(posArr[1]);
|
||||||
|
float calcXPercentage = 0f;
|
||||||
float calcXPercentage = calcPercentage(track.getX(), imageCaptchaTrack.getBgImageWidth());
|
float calcYPercentage = 0f;
|
||||||
float calcYPercentage = calcPercentage(track.getY(), imageCaptchaTrack.getBgImageHeight());
|
if (Boolean.TRUE.equals(checkOrder)) {
|
||||||
if (!checkPercentage(calcXPercentage, xPercentage, tolerant)
|
ImageCaptchaTrack.Track track = clickTrackList.get(0);
|
||||||
|| !checkPercentage(calcYPercentage, yPercentage, tolerant)) {
|
calcXPercentage = calcPercentage(track.getX(), imageCaptchaTrack.getBgImageWidth());
|
||||||
return false;
|
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) {
|
if (i > 0) {
|
||||||
sb.append("|");
|
sb.append("|");
|
||||||
@@ -296,7 +337,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
/**
|
/**
|
||||||
* 校验滑动验证码
|
* 校验滑动验证码
|
||||||
*
|
*
|
||||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
* @param imageCaptchaTrack imageCaptchaTrack
|
||||||
* @param imageCaptchaValidData imageCaptchaValidData
|
* @param imageCaptchaValidData imageCaptchaValidData
|
||||||
* @param tolerant tolerant
|
* @param tolerant tolerant
|
||||||
* @param type type
|
* @param type type
|
||||||
|
|||||||
Reference in New Issue
Block a user