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

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 @EqualsAndHashCode
public class AnyMap implements Map<String, Object> { public class AnyMap implements Map<String, Object> {
private Map<String, Object> target; private final Map<String, Object> target;
public AnyMap() { public AnyMap() {
target = new LinkedHashMap<>(); target = new LinkedHashMap<>();
@@ -77,6 +77,39 @@ public class AnyMap implements Map<String, Object> {
return new AnyMap(map); 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 ======================= // ================== 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.exception.ImageCaptchaException;
import cloud.tianai.captcha.common.util.CollectionUtils; import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange; import cloud.tianai.captcha.generator.common.model.dto.*;
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.util.CaptchaImageUtils; import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.generator.impl.transform.Base64ImageTransform; import cloud.tianai.captcha.generator.impl.transform.Base64ImageTransform;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor; import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
@@ -136,6 +133,11 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaExchange captchaExchange) { public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(captchaExchange); ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(captchaExchange);
imageCaptchaInfo.setData(captchaExchange.getCustomData()); imageCaptchaInfo.setData(captchaExchange.getCustomData());
// 设置自定义容错值
Number tolerant = captchaExchange.getParam().getParam(ParamKeyEnum.TOLERANT);
if (tolerant != null) {
imageCaptchaInfo.setTolerant(tolerant.floatValue());
}
return imageCaptchaInfo; return imageCaptchaInfo;
} }
@@ -1,6 +1,7 @@
package cloud.tianai.captcha.generator.common.model.dto; 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.ParamKey;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -11,89 +12,93 @@ import lombok.EqualsAndHashCode;
* @Description 生成参数 * @Description 生成参数
*/ */
@Data @Data
// param作为扩展字段暂时将param从equals和toString中移除掉 以适应 CacheImageCaptchaGenerator @EqualsAndHashCode(callSuper = true)
@EqualsAndHashCode(exclude = "param") public class GenerateParam extends AnyMap {
public class GenerateParam {
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 static final ParamKey<String> templateImageTag = () -> "templateImageTag";
/**
* 扩展参数.
*/
private AnyMap param = new AnyMap();
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) { public void setTemplateFormatName(String templateFormatName) {
return param == null ? null : param.get(key); addParam(GenerateParam.templateFormatName, templateFormatName);
} }
private AnyMap doGetOrCreateParam() { public void setObfuscate(boolean obfuscate) {
if (param == null) { addParam(GenerateParam.obfuscate, obfuscate);
param = new AnyMap();
}
return param;
} }
public Object removeParam(String key) { public void setType(String type) {
if (param == null) { addParam(GenerateParam.type, type);
return null;
}
return param.remove(key);
}
public <T> Object removeParam(ParamKey<T> paramKey) {
return removeParam(paramKey.getKey());
} }
public Object getOrDefault(String key, Object defaultValue) { public void setBackgroundImageTag(String backgroundImageTag) {
if (param == null) { addParam(GenerateParam.backgroundImageTag, backgroundImageTag);
return defaultValue;
}
return param.getOrDefault(key, defaultValue);
} }
public void setTemplateImageTag(String templateImageTag) {
public Object putIfAbsent(String key, Object value) { addParam(GenerateParam.templateImageTag, templateImageTag);
return doGetOrCreateParam().putIfAbsent(key, value);
} }
public String getBackgroundFormatName() {
public <T> void addParam(ParamKey<T> paramKey, T value) { return getParam(GenerateParam.backgroundFormatName);
addParam(paramKey.getKey(), value);
} }
public <T> T getParam(ParamKey<T> paramKey) { public String getTemplateFormatName() {
return (T) getParam(paramKey.getKey()); return getParam(GenerateParam.templateFormatName);
} }
public <T> T getOrDefault(ParamKey<T> paramKey, T defaultValue) { public boolean getObfuscate() {
return (T) getOrDefault(paramKey.getKey(), defaultValue); 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() { public static Builder builder() {
return new Builder(); return new Builder();
} }
@@ -105,7 +110,6 @@ public class GenerateParam {
private String type = CaptchaTypeConstant.SLIDER; private String type = CaptchaTypeConstant.SLIDER;
private String backgroundImageTag; private String backgroundImageTag;
private String templateImageTag; private String templateImageTag;
private AnyMap param = new AnyMap();
private Builder() { private Builder() {
} }
@@ -140,20 +144,15 @@ public class GenerateParam {
return this; return this;
} }
public Builder param(AnyMap param) {
this.param = param;
return this;
}
public GenerateParam build() { public GenerateParam build() {
GenerateParam generateParam = new GenerateParam(); GenerateParam generateParam = new GenerateParam();
generateParam.backgroundFormatName = backgroundFormatName; generateParam.setBackgroundFormatName(backgroundFormatName);
generateParam.templateFormatName = templateFormatName; generateParam.setTemplateFormatName(templateFormatName);
generateParam.obfuscate = obfuscate; generateParam.setObfuscate(obfuscate);
generateParam.type = type; generateParam.setType(type);
generateParam.backgroundImageTag = backgroundImageTag; generateParam.setBackgroundImageTag(backgroundImageTag);
generateParam.templateImageTag = templateImageTag; generateParam.setTemplateImageTag(templateImageTag);
generateParam.param = param;
return generateParam; 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; package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.ParamKey;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; 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"); public static final ParamKey<Integer> CLICK_INTERFERENCE_COUNT = new ParamKeyEnum<>("interferenceCount");
/** 读取字体时,可指定字体TAG,可用于给不同的验证码指定不同的字体包.*/ /** 读取字体时,可指定字体TAG,可用于给不同的验证码指定不同的字体包.*/
public static final ParamKey<String> FONT_TAG = new ParamKeyEnum<>("fontTag"); public static final ParamKey<String> FONT_TAG = new ParamKeyEnum<>("fontTag");
/** 容错值.*/
public static final ParamKey<Number> TOLERANT = new ParamKeyEnum<>("tolerant");
/** 验证码ID,内部使用.*/ /** 验证码ID,内部使用.*/
public static final ParamKey<String> ID = new ParamKeyEnum<>("_id"); public static final ParamKey<String> ID = new ParamKeyEnum<>("_id");
@@ -1,18 +1,7 @@
package cloud.tianai.captcha.validator.common.model.dto; 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; 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.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -10,22 +12,35 @@ import lombok.NoArgsConstructor;
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class MatchParam { public class MatchParam extends AnyMap {
/** 轨迹信息. */ private static final ParamKey<ImageCaptchaTrack> track = () -> "track";
private ImageCaptchaTrack track;
/** 检测到的设备信息. */
private Drives drives;
/** 留一个扩展属性. */
private Object extendData;
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) { public MatchParam(ImageCaptchaTrack track) {
this.track = track; this.setTrack(track);
} }
public MatchParam(ImageCaptchaTrack track, Drives drives) { public MatchParam(ImageCaptchaTrack track, Drives drives) {
this.track = track; this.setTrack(track);
this.drives = drives; 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_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/active.png"));
// template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/fixed.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() .addDefaultTemplate()
// 设置验证码过期时间 // 设置验证码过期时间