feat(captcha): 优化点选验证码逻辑

- 修改 AbstractClickImageCaptchaGenerator 中的 getClickImg 方法,增加 randomColor 参数
- 更新 MultiImageCaptchaGenerator 中的 StandardWordClickImageCaptchaGenerator 实例创建方式
- 新增 ParamKeyEnum 类,用于定义点选验证码的参数键
- 更新 StandardWordClickImageCaptchaGenerator 中的随机字体选择逻辑
-调整 filterAndSortClickImageCheckDefinition 方法,支持自定义校验数量
This commit is contained in:
天爱有情
2025-03-12 17:35:22 +08:00
parent 5767d98f15
commit 3b1b211629
5 changed files with 69 additions and 39 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>cloud.tianai.captcha</groupId>
<artifactId>tianai-captcha</artifactId>
<version>1.5.1</version>
<version>1.5.2</version>
<name>tianai-captcha</name>
<description>行为验证码</description>
@@ -0,0 +1,19 @@
package cloud.tianai.captcha.generator.common.model.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class ParamKeyEnum<T> implements ParamKey<T> {
/** 点选验证码参与校验的数量. 值为Integer */
public static final ParamKey<Integer> CLICK_CHECK_CLICK_COUNT = new ParamKeyEnum<>("checkClickCount");
/** 点选验证码干扰数量. 值为Integer */
public static final ParamKey<Integer> CLICK_INTERFERENCE_COUNT = new ParamKeyEnum<>("interferenceCount");
private String key;
}
@@ -51,7 +51,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
}
for (int i = 0; i < allImages; i++) {
// 随机获取点击图片
ImgWrapper imgWrapper = getClickImg(imgTips.get(i));
ImgWrapper imgWrapper = getClickImg(imgTips.get(i),null);
BufferedImage image = imgWrapper.getImage();
int clickImgWidth = image.getWidth();
int clickImgHeight = image.getHeight();
@@ -75,7 +75,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
clickImageCheckDefinition.setImageColor(imgWrapper.getImageColor());
clickImageCheckDefinitionList.add(clickImageCheckDefinition);
}
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList);
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(captchaExchange,clickImageCheckDefinitionList);
captchaExchange.setBackgroundImage(bgImage);
captchaExchange.setTransferData(checkClickImageCheckDefinitionList);
captchaExchange.setResourceImage(resourceImage);
@@ -94,7 +94,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
* @param allCheckDefinitionList 总的点选图片
* @return List<ClickImageCheckDefinition>
*/
protected abstract List<ClickImageCheckDefinition> filterAndSortClickImageCheckDefinition(List<ClickImageCheckDefinition> allCheckDefinitionList);
protected abstract List<ClickImageCheckDefinition> filterAndSortClickImageCheckDefinition(CaptchaExchange captchaExchange,List<ClickImageCheckDefinition> allCheckDefinitionList);
/**
* 随机获取一组数据用于生成随机图
@@ -109,7 +109,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
* @param tip 提示数据,根据改数据生成图片
* @return ImgWrapper
*/
public abstract ImgWrapper getClickImg(Resource tip);
public abstract ImgWrapper getClickImg(Resource tip, Color randomColor);
/**
* @Author: 天爱有情
@@ -5,7 +5,6 @@ import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.FontWrapper;
import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
@@ -15,7 +14,6 @@ import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -31,9 +29,9 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
protected Map<String, ImageCaptchaGenerator> imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4);
protected Map<String, ImageCaptchaGeneratorProvider> imageCaptchaGeneratorProviderMap = new HashMap<>(4);
// 点选类验证码字体
@Setter
@Getter
protected List<FontWrapper> fontWrappers;
// @Setter
// @Getter
// protected List<FontWrapper> fontWrappers;
@Setter
@Getter
private String defaultCaptcha = SLIDER;
@@ -56,8 +54,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
// 拼接验证码
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new));
// 点选文字验证码
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, (r, t, i) ->
new StandardWordClickImageCaptchaGenerator(r, t, i, fontWrappers)));
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, StandardWordClickImageCaptchaGenerator::new));
}
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
@@ -1,14 +1,15 @@
package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CommonConstant;
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.FontUtils;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.FontWrapper;
import cloud.tianai.captcha.generator.common.model.dto.*;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
import cloud.tianai.captcha.resource.FontCache;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.Getter;
@@ -30,9 +31,9 @@ import java.util.stream.Collectors;
public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator {
/** 字体包. */
@Getter
@Setter
protected List<FontWrapper> fonts = new ArrayList<>();
// @Getter
// @Setter
// protected List<FontWrapper> fonts = new ArrayList<>();
@Getter
@Setter
protected Integer clickImgWidth = 100;
@@ -64,12 +65,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
//
// protected float currentFontTopCoef = 0.0f;
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager);
}
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform) {
super(imageCaptchaResourceManager);
setImageTransform(imageTransform);
this(imageCaptchaResourceManager, null, null);
}
@@ -79,16 +75,11 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
setInterceptor(interceptor);
}
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform, CaptchaInterceptor interceptor, List<FontWrapper> fonts) {
super(imageCaptchaResourceManager);
setImageTransform(imageTransform);
setInterceptor(interceptor);
this.fonts = fonts;
}
@Override
protected List<Resource> randomGetClickImgTips(GenerateParam param) {
Integer checkClickCount = param.getOrDefault(ParamKeyEnum.CLICK_CHECK_CLICK_COUNT, getCheckClickCount());
Integer interferenceCount = param.getOrDefault(ParamKeyEnum.CLICK_INTERFERENCE_COUNT, getInterferenceCount());
int tipSize = interferenceCount + checkClickCount;
ThreadLocalRandom random = ThreadLocalRandom.current();
List<Resource> tipList = new ArrayList<>(tipSize);
@@ -102,14 +93,25 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
@Override
protected void doInit() {
if (CollectionUtils.isEmpty(fonts)) {
throw new ImageCaptchaException("初始化文字点选验证码失败,请设置字体包后再调用init()");
// if (CollectionUtils.isEmpty(fonts)) {
// throw new ImageCaptchaException("初始化文字点选验证码失败,请设置字体包后再调用init()");
// }
// ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
// // 添加一些系统的资源文件
// resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
}
public FontWrapper randomFont() {
Resource resource = requiredRandomGetResource(FontCache.FONT_TYPE, CommonConstant.DEFAULT_TAG);
Object extra = resource.getExtra();
if (extra instanceof FontWrapper) {
return (FontWrapper) extra;
}
throw new ImageCaptchaException("随机获取字体失败, resource中没有读到字体包, resource=" + resource);
}
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
FontWrapper fontWrapper = fonts.get(randomInt(fonts.size()));
FontWrapper fontWrapper = randomFont();
Font font = fontWrapper.getFont();
float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef();
String tips = imageCheckDefinitions.stream().map(c -> c.getTip().getData()).collect(Collectors.joining());
@@ -124,14 +126,24 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
return new ImgWrapper(bufferedImage, new Resource(null, tips), null);
}
// @Override
// public ImgWrapper getClickImg(Resource tip) {
// ThreadLocalRandom random = ThreadLocalRandom.current();
// // 随机颜色
// Color randomColor = CaptchaImageUtils.getRandomColor(random);
// return getClickImg(tip, randomColor);
// }
@Override
public ImgWrapper getClickImg(Resource tip) {
public ImgWrapper getClickImg(Resource tip, Color randomColor) {
if (randomColor == null) {
ThreadLocalRandom random = ThreadLocalRandom.current();
// 随机颜色
Color randomColor = CaptchaImageUtils.getRandomColor(random);
randomColor = CaptchaImageUtils.getRandomColor(random);
}
// 随机角度
int randomDeg = randomInt(0, 85);
FontWrapper fontWrapper = fonts.get(randomInt(fonts.size()));
FontWrapper fontWrapper = randomFont();
Font font = fontWrapper.getFont();
float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef();
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
@@ -145,7 +157,9 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
}
@Override
protected List<ClickImageCheckDefinition> filterAndSortClickImageCheckDefinition(List<ClickImageCheckDefinition> allCheckDefinitionList) {
protected List<ClickImageCheckDefinition> filterAndSortClickImageCheckDefinition(CaptchaExchange captchaExchange,List<ClickImageCheckDefinition> allCheckDefinitionList) {
GenerateParam param = captchaExchange.getParam();
Integer checkClickCount = param.getOrDefault(ParamKeyEnum.CLICK_CHECK_CLICK_COUNT, getCheckClickCount());
// 打乱
Collections.shuffle(allCheckDefinitionList);
// 拿出参与校验的数据