This commit is contained in:
天爱有情
2022-04-29 18:07:33 +08:00
parent 19d06376a7
commit a4e0def76b
20 changed files with 285 additions and 163 deletions
@@ -15,4 +15,6 @@ public interface CaptchaTypeConstant {
String CONCAT = "CONCAT"; String CONCAT = "CONCAT";
/** 图片点选.*/ /** 图片点选.*/
String IMAGE_CLICK = "IMAGE_CLICK"; String IMAGE_CLICK = "IMAGE_CLICK";
/** 文字图片点选.*/
String WORD_IMAGE_CLICK = "WORD_IMAGE_CLICK";
} }
@@ -11,6 +11,7 @@ public class CaptchaUtils {
/** /**
* 是否是滑动验证码 * 是否是滑动验证码
*
* @param type 类型 * @param type 类型
* @return boolean * @return boolean
*/ */
@@ -22,11 +23,12 @@ public class CaptchaUtils {
/** /**
* 是否是点击验证码 * 是否是点击验证码
*
* @param type type * @param type type
* @return boolean * @return boolean
*/ */
public static boolean isClickCaptcha(String type) { public static boolean isClickCaptcha(String type) {
return CaptchaTypeConstant.IMAGE_CLICK.equals(type); return CaptchaTypeConstant.WORD_IMAGE_CLICK.equals(type) || CaptchaTypeConstant.IMAGE_CLICK.equals(type);
} }
} }
@@ -15,6 +15,7 @@ public interface ImageCaptchaGenerator {
/** /**
* 生成验证码图片 * 生成验证码图片
*
* @param type 类型 {@link CaptchaTypeConstant} * @param type 类型 {@link CaptchaTypeConstant}
* @return SliderCaptchaInfo * @return SliderCaptchaInfo
*/ */
@@ -23,7 +24,8 @@ public interface ImageCaptchaGenerator {
/** /**
* 生成滑块验证码 * 生成滑块验证码
* @param type type {@link CaptchaTypeConstant} *
* @param type type {@link CaptchaTypeConstant}
* @param targetFormatName jpeg或者webp格式 * @param targetFormatName jpeg或者webp格式
* @param matrixFormatName png或者webp格式 * @param matrixFormatName png或者webp格式
* @return SliderCaptchaInfo * @return SliderCaptchaInfo
@@ -34,8 +34,10 @@ public class ImageCaptchaInfo {
private Integer randomX; private Integer randomX;
/** 容错值, 可以为空 默认 0.02容错,校验的时候用. */ /** 容错值, 可以为空 默认 0.02容错,校验的时候用. */
private Float tolerant; private Float tolerant;
/** 验证码类型.*/ /** 验证码类型. */
private String type; private String type;
/** 透传字段,用于传给前端.*/
private Object data;
/** /**
* 扩展字段 * 扩展字段
*/ */
@@ -406,14 +406,15 @@ public class CaptchaImageUtils {
/** /**
* 生成简单的验证码图片 * 生成简单的验证码图片
* @param data 验证码内容 *
* @param font 字体包 * @param data 验证码内容
* @param metrics FontDesignMetrics * @param font 字体包
* @param width 验证码宽度 * @param metrics FontDesignMetrics
* @param height 验证码 * @param width 验证码
* @param startX 起始X * @param height 验证码高度
* @param startY 起始Y * @param startX 起始X
* @param interferenceLineNum 干扰线数量 * @param startY 起始Y
* @param interferenceLineNum 干扰线数量
* @param interferencePointNum 干扰点数量 * @param interferencePointNum 干扰点数量
* @return BufferedImage * @return BufferedImage
*/ */
@@ -450,6 +451,7 @@ public class CaptchaImageUtils {
/** /**
* 随机获取颜色 * 随机获取颜色
*
* @return Color * @return Color
*/ */
public static Color getRandomColor(Random random) { public static Color getRandomColor(Random random) {
@@ -1,23 +1,16 @@
package cloud.tianai.captcha.template.slider.generator.impl; package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ClickImageCheckDefinition; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ClickImageCheckDefinition;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import lombok.AllArgsConstructor; import lombok.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.*; import java.util.*;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -28,12 +21,15 @@ import static cloud.tianai.captcha.template.slider.generator.common.util.Captcha
* @date 2022/4/27 11:46 * @date 2022/4/27 11:46
* @Description 点选验证码 点选验证码分为点选文字和点选图标等 * @Description 点选验证码 点选验证码分为点选文字和点选图标等
*/ */
@Data
public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator { public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/** 参与校验的数量.*/ /** 参与校验的数量. */
@Getter
@Setter
protected Integer checkClickCount = 4; protected Integer checkClickCount = 4;
/** 干扰数量.*/ /** 干扰数量. */
@Getter
@Setter
protected Integer interferenceCount = 2; protected Integer interferenceCount = 2;
@SneakyThrows @SneakyThrows
@@ -49,6 +45,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
List<ClickImageCheckDefinition> clickImageCheckDefinitionList = new ArrayList<>(interferenceCount); List<ClickImageCheckDefinition> clickImageCheckDefinitionList = new ArrayList<>(interferenceCount);
int allImages = interferenceCount + checkClickCount; int allImages = interferenceCount + checkClickCount;
int avg = bgImage.getWidth() / allImages;
for (int i = 0; i < allImages; i++) { for (int i = 0; i < allImages; i++) {
// 随机获取点击图片 // 随机获取点击图片
ImgWrapper imgWrapper = randomGetClickImg(); ImgWrapper imgWrapper = randomGetClickImg();
@@ -56,12 +53,16 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
int clickImgWidth = image.getWidth(); int clickImgWidth = image.getWidth();
int clickImgHeight = image.getHeight(); int clickImgHeight = image.getHeight();
// 随机x // 随机x
int randomX = ThreadLocalRandom.current().nextInt(10, bgImage.getWidth() - clickImgWidth); int randomX;
if (i == 0) {
randomX = 1;
} else {
randomX = avg * i;
}
// 随机y // 随机y
int randomY = ThreadLocalRandom.current().nextInt(10, bgImage.getHeight() - clickImgHeight); int randomY = ThreadLocalRandom.current().nextInt(10, bgImage.getHeight() - clickImgHeight);
// 通过随机x和y 进行覆盖图片 // 通过随机x和y 进行覆盖图片
CaptchaImageUtils.overlayImage(bgImage, imgWrapper.getImage(), randomX, randomY); CaptchaImageUtils.overlayImage(bgImage, imgWrapper.getImage(), randomX, randomY);
ImageIO.write(imgWrapper.getImage(), "png", new FileOutputStream("C:\\Users\\tianai\\Desktop\\111\\" + i + ".png"));
ClickImageCheckDefinition clickImageCheckDefinition = new ClickImageCheckDefinition(); ClickImageCheckDefinition clickImageCheckDefinition = new ClickImageCheckDefinition();
clickImageCheckDefinition.setTip(imgWrapper.getTip()); clickImageCheckDefinition.setTip(imgWrapper.getTip());
clickImageCheckDefinition.setX(randomX + clickImgWidth / 2); clickImageCheckDefinition.setX(randomX + clickImgWidth / 2);
@@ -70,14 +71,6 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
clickImageCheckDefinition.setHeight(clickImgHeight); clickImageCheckDefinition.setHeight(clickImgHeight);
clickImageCheckDefinitionList.add(clickImageCheckDefinition); clickImageCheckDefinitionList.add(clickImageCheckDefinition);
} }
// 背景图转换为字符串
// String bgImageStr = transform(bgImage, param.getBackgroundFormatName());
try {
ImageIO.write(bgImage, "jpeg", new FileOutputStream("C:\\Users\\tianai\\Desktop\\123.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
// 打乱 // 打乱
Collections.shuffle(clickImageCheckDefinitionList); Collections.shuffle(clickImageCheckDefinitionList);
// 拿出参与校验的数据 // 拿出参与校验的数据
@@ -88,12 +81,6 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
} }
// 将校验的文字生成提示图片 // 将校验的文字生成提示图片
ImgWrapper tipImage = genTipImage(checkClickImageCheckDefinitionList); ImgWrapper tipImage = genTipImage(checkClickImageCheckDefinitionList);
try {
ImageIO.write(tipImage.getImage(), "png", new FileOutputStream("C:\\Users\\tianai\\Desktop\\456.png"));
} catch (IOException e) {
e.printStackTrace();
}
return wrapClickImageCaptchaInfo(param, bgImage, tipImage.getImage(), checkClickImageCheckDefinitionList); return wrapClickImageCaptchaInfo(param, bgImage, tipImage.getImage(), checkClickImageCheckDefinitionList);
} finally { } finally {
@@ -110,26 +97,34 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
// return null; // return null;
} }
protected ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
BufferedImage tipImage,
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList) {
ImageCaptchaInfo clickImageCaptchaInfo = new ImageCaptchaInfo();
clickImageCaptchaInfo.setBackgroundImage(transform(bgImage, param.getBackgroundFormatName()));
clickImageCaptchaInfo.setSliderImage(transform(bgImage, param.getSliderFormatName()));
clickImageCaptchaInfo.setBgImageWidth(bgImage.getWidth());
clickImageCaptchaInfo.setBgImageHeight(bgImage.getHeight());
clickImageCaptchaInfo.setSliderImageWidth(tipImage.getWidth());
clickImageCaptchaInfo.setSliderImageHeight(tipImage.getHeight());
clickImageCaptchaInfo.setRandomX(null);
clickImageCaptchaInfo.setTolerant(null);
clickImageCaptchaInfo.setType(CaptchaTypeConstant.IMAGE_CLICK);
clickImageCaptchaInfo.setExpand(checkClickImageCheckDefinitionList);
return clickImageCaptchaInfo;
}
protected abstract ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions); /**
* 随机获取点击的图片
*
* @return ImgWrapper
*/
public abstract ImgWrapper randomGetClickImg();
protected abstract ImgWrapper randomGetClickImg(); /**
* 生成 tip 图片
*
* @param imageCheckDefinitions imageCheckDefinitions
* @return ImgWrapper
*/
public abstract ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions);
/**
* 包装 ImageCaptchaInfo
*
* @param param param
* @param bgImage bgImage
* @param tipImage tipImage
* @param checkClickImageCheckDefinitionList checkClickImageCheckDefinitionList
* @return ImageCaptchaInfo
*/
public abstract ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
BufferedImage tipImage,
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList);
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -140,9 +135,9 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class ImgWrapper { public static class ImgWrapper {
/** 图片.*/ /** 图片. */
private BufferedImage image; private BufferedImage image;
/** 提示.*/ /** 提示. */
private String tip; private String tip;
} }
} }
@@ -136,21 +136,21 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
captchaInfo = queue.poll(); captchaInfo = queue.poll();
if (captchaInfo == null) { if (captchaInfo == null) {
log.warn("滑块验证码缓存不足, genParam:{}", generateParam); log.warn("滑块验证码缓存不足, genParam:{}", generateParam);
}else { } else {
AtomicInteger pos = posMap.get(generateParam); AtomicInteger pos = posMap.get(generateParam);
if (pos != null) { if (pos != null) {
pos.decrementAndGet(); pos.decrementAndGet();
} }
} }
}else { } else {
queueMap.putIfAbsent(generateParam, new ConcurrentLinkedQueue<>()); queueMap.putIfAbsent(generateParam, new ConcurrentLinkedQueue<>());
posMap.putIfAbsent(generateParam, new AtomicInteger(0)); posMap.putIfAbsent(generateParam, new AtomicInteger(0));
} }
if (captchaInfo == null && requiredGetCaptcha) { if (captchaInfo == null && requiredGetCaptcha) {
// 直接生成 不走缓存 // 直接生成 不走缓存
captchaInfo = target.generateCaptchaImage(generateParam); captchaInfo = target.generateCaptchaImage(generateParam);
} }
if (captchaInfo != null){ if (captchaInfo != null) {
// 记录最新时间 // 记录最新时间
lastUpdateMap.put(generateParam, System.currentTimeMillis()); lastUpdateMap.put(generateParam, System.currentTimeMillis());
} }
@@ -1,9 +1,9 @@
package cloud.tianai.captcha.template.slider.generator.impl; package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.common.util.ObjectUtils; import cloud.tianai.captcha.template.slider.common.util.ObjectUtils;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager;
@@ -24,6 +24,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
private boolean initDefaultResource; private boolean initDefaultResource;
private String defaultCaptcha = CaptchaTypeConstant.SLIDER; private String defaultCaptcha = CaptchaTypeConstant.SLIDER;
public MultiImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) { public MultiImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
this.imageCaptchaResourceManager = imageCaptchaResourceManager; this.imageCaptchaResourceManager = imageCaptchaResourceManager;
this.initDefaultResource = initDefaultResource; this.initDefaultResource = initDefaultResource;
@@ -37,11 +38,14 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
addImageCaptchaGenerator(CaptchaTypeConstant.ROTATE, new StandardRotateImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); addImageCaptchaGenerator(CaptchaTypeConstant.ROTATE, new StandardRotateImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource));
// 拼接验证码 // 拼接验证码
addImageCaptchaGenerator(CaptchaTypeConstant.CONCAT, new StandardConcatImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); addImageCaptchaGenerator(CaptchaTypeConstant.CONCAT, new StandardConcatImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource));
// 点选文字验证码
addImageCaptchaGenerator(CaptchaTypeConstant.WORD_IMAGE_CLICK, new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource));
} }
public void addImageCaptchaGenerator(String key, ImageCaptchaGenerator captchaGenerator) { public void addImageCaptchaGenerator(String key, ImageCaptchaGenerator captchaGenerator) {
imageCaptchaGeneratorMap.put(key, captchaGenerator); imageCaptchaGeneratorMap.put(key, captchaGenerator);
} }
public ImageCaptchaGenerator removeImageCaptchaGenerator(String key) { public ImageCaptchaGenerator removeImageCaptchaGenerator(String key) {
return imageCaptchaGeneratorMap.remove(key); return imageCaptchaGeneratorMap.remove(key);
} }
@@ -49,7 +53,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Override @Override
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
String type = param.getType(); String type = param.getType();
if (ObjectUtils.isEmpty(type)){ if (ObjectUtils.isEmpty(type)) {
param.setType(defaultCaptcha); param.setType(defaultCaptcha);
type = defaultCaptcha; type = defaultCaptcha;
} }
@@ -1,7 +1,7 @@
package cloud.tianai.captcha.template.slider.generator.impl; package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager;
@@ -51,18 +51,19 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage); InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream); inputStreams.add(resourceInputStream);
BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream); BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream);
int spacing = bgImage.getHeight() / 4; int spacingY = bgImage.getHeight() / 4;
BufferedImage[] bgImageSplit = splitImage(ThreadLocalRandom.current().nextInt(spacing, bgImage.getHeight() - spacing), true, bgImage); int randomY = ThreadLocalRandom.current().nextInt(spacingY, bgImage.getHeight() - spacingY);
BufferedImage[] bgImageSplit = splitImage(randomY, true, bgImage);
spacing = bgImage.getWidth() / 8; int spacingX = bgImage.getWidth() / 8;
int randomX = ThreadLocalRandom.current().nextInt(spacing, bgImage.getWidth() - spacing); int randomX = ThreadLocalRandom.current().nextInt(spacingX, bgImage.getWidth() - bgImage.getWidth() / 5);
BufferedImage[] bgImageTopSplit = splitImage(randomX, false, bgImageSplit[0]); BufferedImage[] bgImageTopSplit = splitImage(randomX, false, bgImageSplit[0]);
BufferedImage sliderImage = concatImage(true, BufferedImage sliderImage = concatImage(true,
bgImageTopSplit[0].getWidth() bgImageTopSplit[0].getWidth()
+ bgImageTopSplit[1].getWidth() + bgImageTopSplit[1].getWidth(), bgImageTopSplit[0].getHeight(), bgImageTopSplit[1], bgImageTopSplit[0]);
+ bgImageSplit[0].getWidth(), bgImageTopSplit[1].getHeight(), bgImageSplit[0], bgImageTopSplit[0]); bgImage = concatImage(false, bgImageSplit[1].getWidth(), sliderImage.getHeight() + bgImageSplit[1].getHeight(),
return wrapConcatCaptchaInfo(randomX, bgImageSplit[1], sliderImage, param); sliderImage, bgImageSplit[1]);
return wrapConcatCaptchaInfo(randomX, randomY,bgImage, param);
} finally { } finally {
// 使用完后关闭流 // 使用完后关闭流
for (InputStream inputStream : inputStreams) { for (InputStream inputStream : inputStreams) {
@@ -76,17 +77,17 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
} }
@SneakyThrows @SneakyThrows
private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, BufferedImage bgImage, BufferedImage sliderImage, GenerateParam param) { private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, int randomY, BufferedImage bgImage, GenerateParam param) {
String backGroundImageBase64 = transform(bgImage, param.getBackgroundFormatName()); String backGroundImageBase64 = transform(bgImage, param.getBackgroundFormatName());
String sliderImageBase64 = transform(sliderImage, param.getSliderFormatName());
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(backGroundImageBase64, ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(backGroundImageBase64,
sliderImageBase64, null,
bgImage.getWidth(), bgImage.getWidth(),
bgImage.getHeight(), bgImage.getHeight(),
sliderImage.getWidth(), null,
sliderImage.getHeight(), null,
randomX, randomX,
CaptchaTypeConstant.CONCAT); CaptchaTypeConstant.CONCAT);
imageCaptchaInfo.setData(randomY);
imageCaptchaInfo.setTolerant(0.05F); imageCaptchaInfo.setTolerant(0.05F);
return imageCaptchaInfo; return imageCaptchaInfo;
} }
@@ -1,9 +1,11 @@
package cloud.tianai.captcha.template.slider.generator.impl; package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.template.slider.common.util.FontUtils;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.common.util.FontUtils;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ClickImageCheckDefinition; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ClickImageCheckDefinition;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.resource.ResourceStore; import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
@@ -30,11 +32,11 @@ import static cloud.tianai.captcha.template.slider.generator.impl.StandardSlider
public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator { public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator {
protected ImageCaptchaResourceManager imageCaptchaResourceManager; protected ImageCaptchaResourceManager imageCaptchaResourceManager;
/** 字体包.*/ /** 字体包. */
protected Font font; protected Font font;
protected FontDesignMetrics metrics; protected FontDesignMetrics metrics;
protected Integer clickImgWidth = 60; protected Integer clickImgWidth = 80;
protected Integer clickImgHeight = 60; protected Integer clickImgHeight = 80;
protected int tipImageInterferenceLineNum = 2; protected int tipImageInterferenceLineNum = 2;
protected int tipImageInterferencePointNum = 5; protected int tipImageInterferencePointNum = 5;
@@ -48,11 +50,11 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
Resource fontResource = new Resource(null, "META-INF/fonts/SIMSUN.TTC"); Resource fontResource = new Resource(null, "META-INF/fonts/SIMSUN.TTC");
InputStream inputStream = new ClassPathResourceProvider().doGetResourceInputStream(fontResource); InputStream inputStream = new ClassPathResourceProvider().doGetResourceInputStream(fontResource);
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream); Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
font = font.deriveFont(Font.BOLD, 50); font = font.deriveFont(Font.BOLD, 70);
this.metrics = FontDesignMetrics.getMetrics(font); this.metrics = FontDesignMetrics.getMetrics(font);
this.font = font; this.font = font;
setClickImgHeight(60); setClickImgHeight(clickImgWidth);
setClickImgWidth(60); setClickImgWidth(clickImgHeight);
} }
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager,
@@ -71,11 +73,11 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
public void initDefaultResource() { public void initDefaultResource() {
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
// 添加一些系统的资源文件 // 添加一些系统的资源文件
resourceStore.addResource(CaptchaTypeConstant.IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
} }
@Override @Override
protected ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) { public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining()); String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining());
// 生成随机颜色 // 生成随机颜色
int fontWidth = metrics.stringWidth(tips); int fontWidth = metrics.stringWidth(tips);
@@ -89,7 +91,7 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
} }
@Override @Override
protected ImgWrapper randomGetClickImg() { public ImgWrapper randomGetClickImg() {
ThreadLocalRandom random = ThreadLocalRandom.current(); ThreadLocalRandom random = ThreadLocalRandom.current();
// 随机文字 // 随机文字
String randomWord = FontUtils.getRandomChar(random); String randomWord = FontUtils.getRandomChar(random);
@@ -107,6 +109,25 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
return new ImgWrapper(fontImage, randomWord); return new ImgWrapper(fontImage, randomWord);
} }
@Override
public ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
BufferedImage tipImage,
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList) {
ImageCaptchaInfo clickImageCaptchaInfo = new ImageCaptchaInfo();
clickImageCaptchaInfo.setBackgroundImage(transform(bgImage, param.getBackgroundFormatName()));
clickImageCaptchaInfo.setSliderImage(transform(tipImage, param.getSliderFormatName()));
clickImageCaptchaInfo.setBgImageWidth(bgImage.getWidth());
clickImageCaptchaInfo.setBgImageHeight(bgImage.getHeight());
clickImageCaptchaInfo.setSliderImageWidth(tipImage.getWidth());
clickImageCaptchaInfo.setSliderImageHeight(tipImage.getHeight());
clickImageCaptchaInfo.setRandomX(null);
clickImageCaptchaInfo.setTolerant(null);
clickImageCaptchaInfo.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
clickImageCaptchaInfo.setExpand(checkClickImageCheckDefinitionList);
return clickImageCaptchaInfo;
}
@Override @Override
public ImageCaptchaResourceManager getImageResourceManager() { public ImageCaptchaResourceManager getImageResourceManager() {
return imageCaptchaResourceManager; return imageCaptchaResourceManager;
@@ -1,8 +1,7 @@
package cloud.tianai.captcha.template.slider.generator.impl; package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant; import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
@@ -13,10 +12,7 @@ import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider; import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collection; import java.util.Collection;
@@ -61,7 +57,6 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
@Override @Override
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
// 旋转验证码没有混淆 // 旋转验证码没有混淆
Boolean obfuscate = param.getObfuscate();
Map<String, Resource> templateImages = imageCaptchaResourceManager.randomGetTemplate(param.getType()); Map<String, Resource> templateImages = imageCaptchaResourceManager.randomGetTemplate(param.getType());
if (templateImages == null || templateImages.isEmpty()) { if (templateImages == null || templateImages.isEmpty()) {
return null; return null;
@@ -128,36 +123,9 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
); );
} }
// @Override
// public String transform(BufferedImage bufferedImage, String formatType) throws IOException {
// FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\aa" + formatType + "." + formatType);
// ImageIO.write(bufferedImage, formatType, fileOutputStream);
// fileOutputStream.close();
//// return super.transform(bufferedImage, formatType);
// return "";
// }
@Override @Override
public ImageCaptchaResourceManager getImageResourceManager() { public ImageCaptchaResourceManager getImageResourceManager() {
return imageCaptchaResourceManager; return imageCaptchaResourceManager;
} }
public static void main(String[] args) throws IOException {
BufferedImage bgImage = CaptchaImageUtils.wrapFile2BufferedImage(
new FileInputStream("E:\\projects\\tianai-captcha\\src\\main\\resources\\META-INF\\cut-image\\resource\\1.jpg"));
BufferedImage image1 = CaptchaImageUtils.wrapFile2BufferedImage(new FileInputStream("C:\\Users\\Thinkpad\\Desktop\\a.png"));
BufferedImage image2 = CaptchaImageUtils.wrapFile2BufferedImage(new FileInputStream("C:\\Users\\Thinkpad\\Desktop\\b.png"));
BufferedImage cutImage = CaptchaImageUtils.cutImage(bgImage, image1, bgImage.getWidth() / 2 - 100, bgImage.getHeight() / 2 - 100);
CaptchaImageUtils.overlayImage(cutImage, image2, 0, 0);
cutImage = CaptchaImageUtils.rotateImage(cutImage, 180);
CaptchaImageUtils.overlayImage(bgImage, image1, bgImage.getWidth() / 2 - 100, bgImage.getHeight() / 2 - 100);
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\a1.jpg");
FileOutputStream file2OutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\a2.png");
ImageIO.write(bgImage, "jpeg", fileOutputStream);
ImageIO.write(cutImage, "png", file2OutputStream);
fileOutputStream.close();
file2OutputStream.close();
}
} }
@@ -1,7 +1,7 @@
package cloud.tianai.captcha.template.slider.generator.impl; package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant; import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.template.slider.resource; package cloud.tianai.captcha.template.slider.resource;
import cloud.tianai.captcha.template.slider.resource.ResourceProvider;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import java.io.InputStream; import java.io.InputStream;
@@ -31,7 +31,7 @@ public interface ResourceStore {
* *
* @return List<Resource> * @return List<Resource>
*/ */
Map<String,List<Resource>> listAllResources(); Map<String, List<Resource>> listAllResources();
/** /**
* 获取某个type下的所有资源对象 * 获取某个type下的所有资源对象
@@ -0,0 +1,18 @@
package cloud.tianai.captcha.template.slider.validator.common.constant;
/**
* @Author: 天爱有情
* @date 2022/4/29 8:33
* @Description 滑动轨迹类型
*/
public interface TrackTypeConstant {
/** 抬起.*/
String UP = "UP";
/** 按下.*/
String DOWN = "DOWN";
/** 移动.*/
String MOVE = "MOVE";
/** 点击.*/
String CLICK = "CLICK";
}
@@ -1,5 +1,6 @@
package cloud.tianai.captcha.template.slider.validator.common.model.dto; package cloud.tianai.captcha.template.slider.validator.common.model.dto;
import cloud.tianai.captcha.template.slider.validator.common.constant.TrackTypeConstant;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -35,8 +36,13 @@ public class SliderCaptchaTrack {
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class Track { public static class Track {
/** x. */
private Integer x; private Integer x;
/** y. */
private Integer y; private Integer y;
/** 时间. */
private Integer t; private Integer t;
/** 类型. */
private String type = TrackTypeConstant.MOVE;
} }
} }
@@ -1,5 +1,6 @@
package cloud.tianai.captcha.template.slider.validator.impl; package cloud.tianai.captcha.template.slider.validator.impl;
import cloud.tianai.captcha.template.slider.common.util.CaptchaUtils;
import cloud.tianai.captcha.template.slider.common.util.CollectionUtils; import cloud.tianai.captcha.template.slider.common.util.CollectionUtils;
import cloud.tianai.captcha.template.slider.common.util.ObjectUtils; import cloud.tianai.captcha.template.slider.common.util.ObjectUtils;
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack; import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
@@ -22,13 +23,17 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
} }
@Override @Override
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData) { public boolean beforeValid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
// 校验参数 // 校验参数
checkParam(sliderCaptchaTrack); checkParam(sliderCaptchaTrack);
// 基础校验 return true;
boolean superValid = super.valid(sliderCaptchaTrack, sliderCaptchaValidData); }
if (!superValid) {
return false; @Override
public boolean afterValid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
if (!CaptchaUtils.isSliderCaptcha(type)){
// 不是滑动验证码的话暂时跳过,点选验证码行为轨迹还没做
return true;
} }
// 进行行为轨迹检测 // 进行行为轨迹检测
long startSlidingTime = sliderCaptchaTrack.getStartSlidingTime().getTime(); long startSlidingTime = sliderCaptchaTrack.getStartSlidingTime().getTime();
@@ -93,7 +98,6 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
return endAvgPosTime > startAvgPosTime; return endAvgPosTime > startAvgPosTime;
} }
public void checkParam(SliderCaptchaTrack sliderCaptchaTrack) { public void checkParam(SliderCaptchaTrack sliderCaptchaTrack) {
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getBgImageWidth())) { if (ObjectUtils.isEmpty(sliderCaptchaTrack.getBgImageWidth())) {
throw new IllegalArgumentException("bgImageWidth must not be null"); throw new IllegalArgumentException("bgImageWidth must not be null");
@@ -120,8 +124,9 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
Integer x = track.getX(); Integer x = track.getX();
Integer y = track.getY(); Integer y = track.getY();
Integer t = track.getT(); Integer t = track.getT();
if (x == null || y == null || t == null) { String type = track.getType();
throw new IllegalArgumentException("track[x,y,t] must not be null"); if (x == null || y == null || t == null || ObjectUtils.isEmpty(type)) {
throw new IllegalArgumentException("track[x,y,t,type] must not be null");
} }
} }
} }
@@ -7,6 +7,7 @@ import cloud.tianai.captcha.template.slider.common.util.ObjectUtils;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ClickImageCheckDefinition; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ClickImageCheckDefinition;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.validator.ImageCaptchaValidator; import cloud.tianai.captcha.template.slider.validator.ImageCaptchaValidator;
import cloud.tianai.captcha.template.slider.validator.common.constant.TrackTypeConstant;
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack; import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -15,6 +16,7 @@ import lombok.extern.slf4j.Slf4j;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -25,15 +27,15 @@ import java.util.Map;
public class SimpleImageCaptchaValidator implements ImageCaptchaValidator { public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
public static float DEFAULT_TOLERANT = 0.02f; public static float DEFAULT_TOLERANT = 0.02f;
public static final String PERCENTAGE_KEY = "percentage";
public static final String TOLERANT_KEY = "tolerant";
public static final String TYPE_KEY = "type";
/** 容错值. */ /** 容错值. */
@Getter @Getter
@Setter @Setter
public float defaultTolerant = DEFAULT_TOLERANT; public float defaultTolerant = DEFAULT_TOLERANT;
public static final String PERCENTAGE_KEY = "percentage";
public static final String TOLERANT_KEY = "Tolerant";
public static final String TYPE_KEY = "type";
public SimpleImageCaptchaValidator() { public SimpleImageCaptchaValidator() {
} }
@@ -66,6 +68,14 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
@Override @Override
public Map<String, Object> generateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) { public Map<String, Object> generateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
Map<String, Object> map = new HashMap<>(8); Map<String, Object> map = new HashMap<>(8);
if(beforeGenerateImageCaptchaValidData(imageCaptchaInfo, map)) {
doGenerateImageCaptchaValidData(map, imageCaptchaInfo);
}
afterGenerateImageCaptchaValidData(imageCaptchaInfo, map);
return map;
}
public boolean beforeGenerateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> map) {
// 容错值 // 容错值
Float tolerant = imageCaptchaInfo.getTolerant(); Float tolerant = imageCaptchaInfo.getTolerant();
if (tolerant != null && tolerant > 0) { if (tolerant != null && tolerant > 0) {
@@ -77,13 +87,17 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
type = CaptchaTypeConstant.SLIDER; type = CaptchaTypeConstant.SLIDER;
} }
map.put(TYPE_KEY, type); map.put(TYPE_KEY, type);
return true;
return afterGenerateImageCaptchaValidData(map, imageCaptchaInfo, type);
} }
public Map<String, Object> afterGenerateImageCaptchaValidData(Map<String, Object> map, public void afterGenerateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> map) {
ImageCaptchaInfo imageCaptchaInfo,
String type) { }
public void doGenerateImageCaptchaValidData(Map<String, Object> map,
ImageCaptchaInfo imageCaptchaInfo) {
// type
String type = (String) map.getOrDefault(TYPE_KEY, CaptchaTypeConstant.SLIDER);
if (CaptchaUtils.isSliderCaptcha(type)) { if (CaptchaUtils.isSliderCaptcha(type)) {
// 滑动验证码 // 滑动验证码
addPercentage(imageCaptchaInfo, map); addPercentage(imageCaptchaInfo, map);
@@ -93,7 +107,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
if (expand == null) { if (expand == null) {
throw new IllegalArgumentException("点选验证码扩展数据转换为 List<ClickImageCheckDefinition> 失败, info=" + imageCaptchaInfo); throw new IllegalArgumentException("点选验证码扩展数据转换为 List<ClickImageCheckDefinition> 失败, info=" + imageCaptchaInfo);
} }
List<ClickImageCheckDefinition> clickImageCheckDefinitionList = null; List<ClickImageCheckDefinition> clickImageCheckDefinitionList;
try { try {
clickImageCheckDefinitionList = (List<ClickImageCheckDefinition>) expand; clickImageCheckDefinitionList = (List<ClickImageCheckDefinition>) expand;
} catch (Exception e) { } catch (Exception e) {
@@ -116,17 +130,15 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
map.put(TOLERANT_KEY, tolerant); map.put(TOLERANT_KEY, tolerant);
} }
} }
// 添加点选验证 // 添加点选验证数据
map.put(PERCENTAGE_KEY, sb.toString()); map.put(PERCENTAGE_KEY, sb.toString());
} }
return map;
} }
@Override @Override
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData) { public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData) {
// 读容错值 // 读容错值
Float tolerantData = getFloatParam(TOLERANT_KEY, sliderCaptchaValidData, defaultTolerant); Float tolerant = getFloatParam(TOLERANT_KEY, sliderCaptchaValidData, defaultTolerant);
// 读验证码类型 // 读验证码类型
String type = getStringParam(TYPE_KEY, sliderCaptchaValidData, CaptchaTypeConstant.SLIDER); String type = getStringParam(TYPE_KEY, sliderCaptchaValidData, CaptchaTypeConstant.SLIDER);
Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth(); Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
@@ -139,13 +151,49 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
// 没有滑动轨迹 // 没有滑动轨迹
return false; return false;
} }
return afterValid(sliderCaptchaTrack, sliderCaptchaValidData, tolerantData, type); // 验证前
if (!beforeValid(sliderCaptchaTrack, sliderCaptchaValidData, tolerant, type)) {
return false;
}
// 验证
boolean valid = doValid(sliderCaptchaTrack, sliderCaptchaValidData, tolerant, type);
if (valid) {
// 验证后
valid = afterValid(sliderCaptchaTrack, sliderCaptchaValidData, tolerant, type);
}
return valid;
} }
public boolean afterValid(SliderCaptchaTrack sliderCaptchaTrack, /**
Map<String, Object> sliderCaptchaValidData, * 验证前
Float tolerant, *
String type) { * @param sliderCaptchaTrack sliderCaptchaTrack
* @param sliderCaptchaValidData sliderCaptchaValidData
* @param tolerant tolerant
* @param type type
* @return boolean
*/
public boolean beforeValid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
return true;
}
/**
* 验证后
*
* @param sliderCaptchaTrack sliderCaptchaTrack
* @param sliderCaptchaValidData sliderCaptchaValidData
* @param tolerant tolerant
* @param type type
* @return boolean
*/
public boolean afterValid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
return true;
}
public boolean doValid(SliderCaptchaTrack sliderCaptchaTrack,
Map<String, Object> sliderCaptchaValidData,
Float tolerant,
String type) {
if (CaptchaUtils.isSliderCaptcha(type)) { if (CaptchaUtils.isSliderCaptcha(type)) {
// 滑动类型验证码 // 滑动类型验证码
return doValidSliderCaptcha(sliderCaptchaTrack, sliderCaptchaValidData, tolerant, type); return doValidSliderCaptcha(sliderCaptchaTrack, sliderCaptchaValidData, tolerant, type);
@@ -160,10 +208,11 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
/** /**
* 校验点选验证码 * 校验点选验证码
* @param sliderCaptchaTrack sliderCaptchaTrack *
* @param sliderCaptchaTrack sliderCaptchaTrack
* @param sliderCaptchaValidData sliderCaptchaValidData * @param sliderCaptchaValidData sliderCaptchaValidData
* @param tolerant tolerant * @param tolerant tolerant
* @param type type * @param type type
* @return boolean * @return boolean
*/ */
public boolean doValidClickCaptcha(SliderCaptchaTrack sliderCaptchaTrack, public boolean doValidClickCaptcha(SliderCaptchaTrack sliderCaptchaTrack,
@@ -176,18 +225,26 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
} }
String[] splitArr = validStr.split(";"); String[] splitArr = validStr.split(";");
List<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList(); List<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList();
if (trackList.size() != splitArr.length) { if (trackList.size() < splitArr.length) {
return false;
}
// 取出点击事件的轨迹数据
List<SliderCaptchaTrack.Track> clickTrackList = trackList
.stream()
.filter(t -> TrackTypeConstant.CLICK.equalsIgnoreCase(t.getType()))
.collect(Collectors.toList());
if (clickTrackList.size() != splitArr.length) {
return false; return false;
} }
for (int i = 0; i < splitArr.length; i++) { for (int i = 0; i < splitArr.length; i++) {
SliderCaptchaTrack.Track track = trackList.get(i); SliderCaptchaTrack.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 = calcPercentage(track.getX(), sliderCaptchaTrack.getBgImageWidth()); float calcXPercentage = calcPercentage(track.getX(), sliderCaptchaTrack.getBgImageWidth());
float calcYPercentage = calcPercentage(track.getY(), sliderCaptchaTrack.getBgImageWidth()); float calcYPercentage = calcPercentage(track.getY(), sliderCaptchaTrack.getBgImageHeight());
if (!checkPercentage(calcXPercentage, xPercentage, tolerant) if (!checkPercentage(calcXPercentage, xPercentage, tolerant)
|| !checkPercentage(calcYPercentage, yPercentage, tolerant)) { || !checkPercentage(calcYPercentage, yPercentage, tolerant)) {
@@ -199,10 +256,11 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
/** /**
* 校验滑动验证码 * 校验滑动验证码
* @param sliderCaptchaTrack sliderCaptchaTrack *
* @param sliderCaptchaTrack sliderCaptchaTrack
* @param sliderCaptchaValidData sliderCaptchaValidData * @param sliderCaptchaValidData sliderCaptchaValidData
* @param tolerant tolerant * @param tolerant tolerant
* @param type type * @param type type
* @return boolean * @return boolean
*/ */
public boolean doValidSliderCaptcha(SliderCaptchaTrack sliderCaptchaTrack, public boolean doValidSliderCaptcha(SliderCaptchaTrack sliderCaptchaTrack,
@@ -0,0 +1,37 @@
package example;
import cloud.tianai.captcha.template.slider.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.RotateImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.impl.CacheImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.impl.StandardConcatImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.impl.StandardRotateImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.impl.DefaultImageCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.resource.impl.DefaultResourceStore;
import java.util.concurrent.TimeUnit;
public class StandardConcatCaptchaGeneratorTest {
public static void main(String[] args) throws InterruptedException {
ResourceStore resourceStore = new DefaultResourceStore();
// Map<String, Resource> template = new HashMap<>();
// template.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\a.png"));
// template.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\b.png"));
// resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template);
// resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource("file", "E:\\projects\\tianai-captcha\\src\\main\\resources\\META-INF\\cut-image\\resource\\1.jpg"));
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager(resourceStore);
StandardConcatImageCaptchaGenerator captchaGenerator = new StandardConcatImageCaptchaGenerator(imageCaptchaResourceManager, true);
ImageCaptchaInfo imageCaptchaInfo = captchaGenerator.generateCaptchaImage(CaptchaTypeConstant.CONCAT);
System.out.println("backgroundImage:" + imageCaptchaInfo.getBackgroundImage());
System.out.println("sliderImage:" + imageCaptchaInfo.getSliderImage());
TimeUnit.DAYS.sleep(1);
}
}
@@ -19,7 +19,7 @@ public class StandardWordClickImageCaptchaGeneratorTest {
new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, true); new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, true);
GenerateParam generateParam = new GenerateParam(); GenerateParam generateParam = new GenerateParam();
generateParam.setType(CaptchaTypeConstant.IMAGE_CLICK); generateParam.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
generateParam.setSliderFormatName("png"); generateParam.setSliderFormatName("png");
ImageCaptchaInfo imageCaptchaInfo = defaultImageCaptchaResourceManager.generateCaptchaImage(generateParam); ImageCaptchaInfo imageCaptchaInfo = defaultImageCaptchaResourceManager.generateCaptchaImage(generateParam);