增加可配置自定义容错值的选项

cloud.tianai.captcha.generator.common.model.dto.ParamKeyEnum#TOLERANT
This commit is contained in:
天爱有情
2026-01-23 17:24:25 +08:00
parent 16e517c69e
commit a4f8a99093
9 changed files with 141 additions and 100 deletions
@@ -13,7 +13,7 @@ import java.util.function.Function;
@EqualsAndHashCode
public class AnyMap implements Map<String, Object> {
private Map<String, Object> target;
private final Map<String, Object> target;
public AnyMap() {
target = new LinkedHashMap<>();
@@ -77,6 +77,39 @@ public class AnyMap implements Map<String, Object> {
return new AnyMap(map);
}
public void addParam(String key, Object value) {
put(key, value);
}
public Object getParam(String key) {
return get(key);
}
public Object removeParam(String key) {
return remove(key);
}
public <T> Object removeParam(ParamKey<T> paramKey) {
return removeParam(paramKey.getKey());
}
public <T> void addParam(ParamKey<T> paramKey, T value) {
addParam(paramKey.getKey(), value);
}
public <T> T getParam(ParamKey<T> paramKey) {
return getParam(paramKey, null);
}
public <T> T getParam(ParamKey<T> paramKey, T defaultValue) {
return (T) getParam(paramKey.getKey());
}
public <T> T getOrDefault(ParamKey<T> paramKey, T defaultValue) {
return (T) getOrDefault(paramKey.getKey(), defaultValue);
}
// ================== implement Map =======================
@@ -0,0 +1,12 @@
package cloud.tianai.captcha.common;
/**
* @Author: 天爱有情
* @date 2024/11/20 11:34
* @Description 此接口的作用是在给 {@link AnyMap} 添加/获取参数时做一个类型限制和转换
*/
public interface ParamKey<T> {
String getKey();
}
@@ -2,10 +2,7 @@ package cloud.tianai.captcha.generator;
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
import cloud.tianai.captcha.generator.common.model.dto.CustomData;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.model.dto.*;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.generator.impl.transform.Base64ImageTransform;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
@@ -136,6 +133,11 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(captchaExchange);
imageCaptchaInfo.setData(captchaExchange.getCustomData());
// 设置自定义容错值
Number tolerant = captchaExchange.getParam().getParam(ParamKeyEnum.TOLERANT);
if (tolerant != null) {
imageCaptchaInfo.setTolerant(tolerant.floatValue());
}
return imageCaptchaInfo;
}
@@ -1,6 +1,7 @@
package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.AnyMap;
import cloud.tianai.captcha.common.ParamKey;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -11,89 +12,93 @@ import lombok.EqualsAndHashCode;
* @Description 生成参数
*/
@Data
// param作为扩展字段暂时将param从equals和toString中移除掉 以适应 CacheImageCaptchaGenerator
@EqualsAndHashCode(exclude = "param")
public class GenerateParam {
@EqualsAndHashCode(callSuper = true)
public class GenerateParam extends AnyMap {
public GenerateParam() {
// 设置一些默认值
setBackgroundFormatName("jpeg");
setTemplateFormatName("png");
setObfuscate(false);
setType(CaptchaTypeConstant.SLIDER);
}
/**
* 背景格式化类型.
*/
private String backgroundFormatName = "jpeg";
private static final ParamKey<String> backgroundFormatName = () -> "backgroundFormatName";
/**
* 模板图片格式化类型.
*/
private String templateFormatName = "png";
private static final ParamKey<String> templateFormatName = () -> "templateFormatName";
/**
* 是否混淆.
*/
private Boolean obfuscate = false;
private static final ParamKey<Boolean> obfuscate = () -> "obfuscate";
/**
* 类型.
*/
private String type = CaptchaTypeConstant.SLIDER;
private static final ParamKey<String> type = () -> "type";
/**
* 背景图片标签, 用户二级过滤背景图片,或指定某背景图片.
*/
private String backgroundImageTag;
private static final ParamKey<String> backgroundImageTag = () -> "backgroundImageTag";
/**
* 滑动图片标签,用户二级过滤模板图片,或指定某模板图片..
*/
private String templateImageTag;
/**
* 扩展参数.
*/
private AnyMap param = new AnyMap();
private static final ParamKey<String> templateImageTag = () -> "templateImageTag";
public void addParam(String key, Object value) {
doGetOrCreateParam().put(key, value);
// =============== getter and setter ====================
public void setBackgroundFormatName(String backgroundFormatName) {
addParam(GenerateParam.backgroundFormatName, backgroundFormatName);
}
public Object getParam(String key) {
return param == null ? null : param.get(key);
public void setTemplateFormatName(String templateFormatName) {
addParam(GenerateParam.templateFormatName, templateFormatName);
}
private AnyMap doGetOrCreateParam() {
if (param == null) {
param = new AnyMap();
}
return param;
public void setObfuscate(boolean obfuscate) {
addParam(GenerateParam.obfuscate, obfuscate);
}
public Object removeParam(String key) {
if (param == null) {
return null;
}
return param.remove(key);
}
public <T> Object removeParam(ParamKey<T> paramKey) {
return removeParam(paramKey.getKey());
public void setType(String type) {
addParam(GenerateParam.type, type);
}
public Object getOrDefault(String key, Object defaultValue) {
if (param == null) {
return defaultValue;
}
return param.getOrDefault(key, defaultValue);
public void setBackgroundImageTag(String backgroundImageTag) {
addParam(GenerateParam.backgroundImageTag, backgroundImageTag);
}
public Object putIfAbsent(String key, Object value) {
return doGetOrCreateParam().putIfAbsent(key, value);
public void setTemplateImageTag(String templateImageTag) {
addParam(GenerateParam.templateImageTag, templateImageTag);
}
public <T> void addParam(ParamKey<T> paramKey, T value) {
addParam(paramKey.getKey(), value);
public String getBackgroundFormatName() {
return getParam(GenerateParam.backgroundFormatName);
}
public <T> T getParam(ParamKey<T> paramKey) {
return (T) getParam(paramKey.getKey());
public String getTemplateFormatName() {
return getParam(GenerateParam.templateFormatName);
}
public <T> T getOrDefault(ParamKey<T> paramKey, T defaultValue) {
return (T) getOrDefault(paramKey.getKey(), defaultValue);
public boolean getObfuscate() {
return getParam(GenerateParam.obfuscate);
}
public String getType() {
return getParam(GenerateParam.type);
}
public String getBackgroundImageTag() {
return getParam(GenerateParam.backgroundImageTag);
}
public String getTemplateImageTag() {
return getParam(GenerateParam.templateImageTag);
}
public static Builder builder() {
return new Builder();
}
@@ -105,7 +110,6 @@ public class GenerateParam {
private String type = CaptchaTypeConstant.SLIDER;
private String backgroundImageTag;
private String templateImageTag;
private AnyMap param = new AnyMap();
private Builder() {
}
@@ -140,20 +144,15 @@ public class GenerateParam {
return this;
}
public Builder param(AnyMap param) {
this.param = param;
return this;
}
public GenerateParam build() {
GenerateParam generateParam = new GenerateParam();
generateParam.backgroundFormatName = backgroundFormatName;
generateParam.templateFormatName = templateFormatName;
generateParam.obfuscate = obfuscate;
generateParam.type = type;
generateParam.backgroundImageTag = backgroundImageTag;
generateParam.templateImageTag = templateImageTag;
generateParam.param = param;
generateParam.setBackgroundFormatName(backgroundFormatName);
generateParam.setTemplateFormatName(templateFormatName);
generateParam.setObfuscate(obfuscate);
generateParam.setType(type);
generateParam.setBackgroundImageTag(backgroundImageTag);
generateParam.setTemplateImageTag(templateImageTag);
return generateParam;
}
}
@@ -1,12 +0,0 @@
package cloud.tianai.captcha.generator.common.model.dto;
/**
* @Author: 天爱有情
* @date 2024/11/20 11:34
* @Description 此接口的作用是在给 {@link GenerateParam} 添加/获取参数时做一个类型限制和转换
*/
public interface ParamKey<T> {
String getKey();
}
@@ -1,6 +1,7 @@
package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.ParamKey;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -15,7 +16,8 @@ public class ParamKeyEnum<T> implements ParamKey<T> {
public static final ParamKey<Integer> CLICK_INTERFERENCE_COUNT = new ParamKeyEnum<>("interferenceCount");
/** 读取字体时,可指定字体TAG,可用于给不同的验证码指定不同的字体包.*/
public static final ParamKey<String> FONT_TAG = new ParamKeyEnum<>("fontTag");
/** 容错值.*/
public static final ParamKey<Number> TOLERANT = new ParamKeyEnum<>("tolerant");
/** 验证码ID,内部使用.*/
public static final ParamKey<String> ID = new ParamKeyEnum<>("_id");
@@ -1,18 +1,7 @@
package cloud.tianai.captcha.validator.common.model.dto;
import lombok.Data;
import cloud.tianai.captcha.common.AnyMap;
public class Drives extends AnyMap {
@Data
public class Drives {
private Integer hardwareConcurrency;
private Boolean hasXhr = false;
private String href;
private String language;
private Long start;
private Long now;
private String platform;
private Integer scripts;
private String userAgent;
private Integer windowHeight;
private Integer windowWidth;
}
@@ -1,5 +1,7 @@
package cloud.tianai.captcha.validator.common.model.dto;
import cloud.tianai.captcha.common.AnyMap;
import cloud.tianai.captcha.common.ParamKey;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -10,22 +12,35 @@ import lombok.NoArgsConstructor;
*/
@Data
@NoArgsConstructor
public class MatchParam {
/** 轨迹信息. */
private ImageCaptchaTrack track;
/** 检测到的设备信息. */
private Drives drives;
/** 留一个扩展属性. */
private Object extendData;
public class MatchParam extends AnyMap {
private static final ParamKey<ImageCaptchaTrack> track = () -> "track";
private static final ParamKey<Drives> drives = () -> "drives";
public ImageCaptchaTrack getTrack() {
return getParam(MatchParam.track);
}
public Drives getDrives() {
return getParam(MatchParam.drives);
}
public void setTrack(ImageCaptchaTrack track) {
addParam(MatchParam.track, track);
}
public void setDrives(Drives drives) {
addParam(MatchParam.drives, drives);
}
public MatchParam(ImageCaptchaTrack track) {
this.track = track;
this.setTrack(track);
}
public MatchParam(ImageCaptchaTrack track, Drives drives) {
this.track = track;
this.drives = drives;
this.setTrack(track);
this.setDrives(drives);
}
}
@@ -26,7 +26,8 @@ public class TACBuilderTest {
// template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/active.png"));
// template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/fixed.png"));
ImageCaptchaApplication application = TACBuilder.builder(new LocalMemoryResourceStore())
ImageCaptchaApplication application = TACBuilder.builder()
.setResourceStore(new LocalMemoryResourceStore())
// 加载系统自带的默认资源
.addDefaultTemplate()
// 设置验证码过期时间