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> <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.1</version> <version>1.5.2</version>
<name>tianai-captcha</name> <name>tianai-captcha</name>
<description>行为验证码</description> <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++) { for (int i = 0; i < allImages; i++) {
// 随机获取点击图片 // 随机获取点击图片
ImgWrapper imgWrapper = getClickImg(imgTips.get(i)); ImgWrapper imgWrapper = getClickImg(imgTips.get(i),null);
BufferedImage image = imgWrapper.getImage(); BufferedImage image = imgWrapper.getImage();
int clickImgWidth = image.getWidth(); int clickImgWidth = image.getWidth();
int clickImgHeight = image.getHeight(); int clickImgHeight = image.getHeight();
@@ -75,7 +75,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
clickImageCheckDefinition.setImageColor(imgWrapper.getImageColor()); clickImageCheckDefinition.setImageColor(imgWrapper.getImageColor());
clickImageCheckDefinitionList.add(clickImageCheckDefinition); clickImageCheckDefinitionList.add(clickImageCheckDefinition);
} }
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList); List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(captchaExchange,clickImageCheckDefinitionList);
captchaExchange.setBackgroundImage(bgImage); captchaExchange.setBackgroundImage(bgImage);
captchaExchange.setTransferData(checkClickImageCheckDefinitionList); captchaExchange.setTransferData(checkClickImageCheckDefinitionList);
captchaExchange.setResourceImage(resourceImage); captchaExchange.setResourceImage(resourceImage);
@@ -94,7 +94,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
* @param allCheckDefinitionList 总的点选图片 * @param allCheckDefinitionList 总的点选图片
* @return List<ClickImageCheckDefinition> * @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 提示数据,根据改数据生成图片 * @param tip 提示数据,根据改数据生成图片
* @return ImgWrapper * @return ImgWrapper
*/ */
public abstract ImgWrapper getClickImg(Resource tip); public abstract ImgWrapper getClickImg(Resource tip, Color randomColor);
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -5,7 +5,6 @@ import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider; import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.ImageTransform; 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.CaptchaExchange;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam; 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.ImageCaptchaInfo;
@@ -15,7 +14,6 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; 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, ImageCaptchaGenerator> imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4);
protected Map<String, ImageCaptchaGeneratorProvider> imageCaptchaGeneratorProviderMap = new HashMap<>(4); protected Map<String, ImageCaptchaGeneratorProvider> imageCaptchaGeneratorProviderMap = new HashMap<>(4);
// 点选类验证码字体 // 点选类验证码字体
@Setter // @Setter
@Getter // @Getter
protected List<FontWrapper> fontWrappers; // protected List<FontWrapper> fontWrappers;
@Setter @Setter
@Getter @Getter
private String defaultCaptcha = SLIDER; private String defaultCaptcha = SLIDER;
@@ -56,8 +54,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
// 拼接验证码 // 拼接验证码
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new)); addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new));
// 点选文字验证码 // 点选文字验证码
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, (r, t, i) -> addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, StandardWordClickImageCaptchaGenerator::new));
new StandardWordClickImageCaptchaGenerator(r, t, i, fontWrappers)));
} }
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) { public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
@@ -1,14 +1,15 @@
package cloud.tianai.captcha.generator.impl; package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant; 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.exception.ImageCaptchaException;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.FontUtils; import cloud.tianai.captcha.common.util.FontUtils;
import cloud.tianai.captcha.generator.ImageTransform; import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.FontWrapper; import cloud.tianai.captcha.generator.common.FontWrapper;
import cloud.tianai.captcha.generator.common.model.dto.*; import cloud.tianai.captcha.generator.common.model.dto.*;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils; import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor; import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
import cloud.tianai.captcha.resource.FontCache;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager; 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 lombok.Getter; import lombok.Getter;
@@ -30,9 +31,9 @@ import java.util.stream.Collectors;
public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator { public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator {
/** 字体包. */ /** 字体包. */
@Getter // @Getter
@Setter // @Setter
protected List<FontWrapper> fonts = new ArrayList<>(); // protected List<FontWrapper> fonts = new ArrayList<>();
@Getter @Getter
@Setter @Setter
protected Integer clickImgWidth = 100; protected Integer clickImgWidth = 100;
@@ -64,12 +65,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
// //
// protected float currentFontTopCoef = 0.0f; // protected float currentFontTopCoef = 0.0f;
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) { public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager); this(imageCaptchaResourceManager, null, null);
}
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform) {
super(imageCaptchaResourceManager);
setImageTransform(imageTransform);
} }
@@ -79,16 +75,11 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
setInterceptor(interceptor); setInterceptor(interceptor);
} }
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform, CaptchaInterceptor interceptor, List<FontWrapper> fonts) {
super(imageCaptchaResourceManager);
setImageTransform(imageTransform);
setInterceptor(interceptor);
this.fonts = fonts;
}
@Override @Override
protected List<Resource> randomGetClickImgTips(GenerateParam param) { 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; int tipSize = interferenceCount + checkClickCount;
ThreadLocalRandom random = ThreadLocalRandom.current(); ThreadLocalRandom random = ThreadLocalRandom.current();
List<Resource> tipList = new ArrayList<>(tipSize); List<Resource> tipList = new ArrayList<>(tipSize);
@@ -102,14 +93,25 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
@Override @Override
protected void doInit() { protected void doInit() {
if (CollectionUtils.isEmpty(fonts)) { // if (CollectionUtils.isEmpty(fonts)) {
throw new ImageCaptchaException("初始化文字点选验证码失败,请设置字体包后再调用init()"); // 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) { public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
FontWrapper fontWrapper = fonts.get(randomInt(fonts.size())); FontWrapper fontWrapper = randomFont();
Font font = fontWrapper.getFont(); Font font = fontWrapper.getFont();
float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef(); float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef();
String tips = imageCheckDefinitions.stream().map(c -> c.getTip().getData()).collect(Collectors.joining()); 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); 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 @Override
public ImgWrapper getClickImg(Resource tip) { public ImgWrapper getClickImg(Resource tip, Color randomColor) {
if (randomColor == null) {
ThreadLocalRandom random = ThreadLocalRandom.current(); ThreadLocalRandom random = ThreadLocalRandom.current();
// 随机颜色 randomColor = CaptchaImageUtils.getRandomColor(random);
Color randomColor = CaptchaImageUtils.getRandomColor(random); }
// 随机角度 // 随机角度
int randomDeg = randomInt(0, 85); int randomDeg = randomInt(0, 85);
FontWrapper fontWrapper = fonts.get(randomInt(fonts.size())); FontWrapper fontWrapper = randomFont();
Font font = fontWrapper.getFont(); Font font = fontWrapper.getFont();
float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef(); float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef();
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor, BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
@@ -145,7 +157,9 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
} }
@Override @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); Collections.shuffle(allCheckDefinitionList);
// 拿出参与校验的数据 // 拿出参与校验的数据