修复以及优化很多

This commit is contained in:
天爱有情
2023-11-09 16:40:28 +08:00
parent 7e0e6bc0b6
commit db2f409783
42 changed files with 403 additions and 606 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.4.2</version> <version>1.5.0</version>
<name>tianai-captcha</name> <name>tianai-captcha</name>
<description>行为验证码</description> <description>行为验证码</description>
@@ -11,19 +11,8 @@ public interface CaptchaTypeConstant {
String SLIDER = "SLIDER"; String SLIDER = "SLIDER";
/** 旋转. */ /** 旋转. */
String ROTATE = "ROTATE"; String ROTATE = "ROTATE";
/** 旋转角度.*/ /** 拼接. */
String ROTATE_DEGREE = "ROTATE_DEGREE";
/** 拼接.*/
String CONCAT = "CONCAT"; String CONCAT = "CONCAT";
/** 拼图.*/ /** 文字图片点选. */
String JIGSAW = "JIGSAW";
/** 图片点选.*/
String IMAGE_CLICK = "IMAGE_CLICK";
/** 文字图片点选.*/
String WORD_IMAGE_CLICK = "WORD_IMAGE_CLICK"; String WORD_IMAGE_CLICK = "WORD_IMAGE_CLICK";
/** 语序点选.*/
String WORD_ORDER_IMAGE_CLICK = "WORD_ORDER_IMAGE_CLICK";
} }
@@ -3,6 +3,20 @@ package cloud.tianai.captcha.common.constant;
public interface CommonConstant { public interface CommonConstant {
String DEFAULT_TAG = "default"; String DEFAULT_TAG = "default";
/** 图标点选资源存储类型.*/ /** 图标点选资源存储类型. */
String IMAGE_CLICK_ICON = "ICON"; String IMAGE_CLICK_ICON = "ICON";
/** 蜂窝点选.*/
String HONEYCOMB_CLICK_ICON = "HONEYCOMB_ICON";
/** 刮刮卡图标. */
String SCRAPE_ICON = "SCRAPE_ICON";
/**
* 默认的resource资源文件路径.
*/
String DEFAULT_SLIDER_IMAGE_RESOURCE_PATH = "META-INF/cut-image/resource";
/**
* 默认的template资源文件路径.
*/
String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template";
} }
@@ -0,0 +1,64 @@
package cloud.tianai.captcha.common.util;
import java.util.HashSet;
import java.util.Set;
/**
* @Author: 天爱有情
* @date 2023/11/2 9:22
* @Description 验证码类型分类
*/
public class CaptchaTypeClassifier {
private static final Set<String> SLIDER_CAPTCHA_TYPES = new HashSet<>();
private static final Set<String> CLICK_CAPTCHA_TYPES = new HashSet<>();
private static final Set<String> JIGSAW_CAPTCHA_TYPES = new HashSet<>();
public static void addSliderCaptchaType(String type) {
SLIDER_CAPTCHA_TYPES.add(type.toUpperCase());
}
public static void addClickCaptchaType(String type) {
CLICK_CAPTCHA_TYPES.add(type.toUpperCase());
}
public static boolean isSliderCaptcha(String type) {
return SLIDER_CAPTCHA_TYPES.contains(type.toUpperCase());
}
public static boolean isClickCaptcha(String type) {
return CLICK_CAPTCHA_TYPES.contains(type.toUpperCase());
}
public static Set<String> getSliderCaptchaTypes() {
return SLIDER_CAPTCHA_TYPES;
}
public static Set<String> getClickCaptchaTypes() {
return CLICK_CAPTCHA_TYPES;
}
public static void removeSliderCaptchaType(String type) {
SLIDER_CAPTCHA_TYPES.remove(type.toUpperCase());
}
public static void removeClickCaptchaType(String type) {
CLICK_CAPTCHA_TYPES.remove(type.toUpperCase());
}
public static boolean isJigsawCaptcha(String type) {
return JIGSAW_CAPTCHA_TYPES.contains(type.toUpperCase());
}
public static void addJigsawCaptchaType(String type) {
JIGSAW_CAPTCHA_TYPES.add(type.toUpperCase());
}
public static void removeJigsawCaptchaType(String type) {
JIGSAW_CAPTCHA_TYPES.remove(type.toUpperCase());
}
public static Set<String> getJigsawCaptchaTypes() {
return JIGSAW_CAPTCHA_TYPES;
}
}
@@ -1,44 +0,0 @@
package cloud.tianai.captcha.common.util;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
/**
* @Author: 天爱有情
* @date 2022/4/28 17:03
* @Description 验证码工具包
*/
public class CaptchaUtils {
/**
* 是否是滑动验证码
*
* @param type 类型
* @return boolean
*/
public static boolean isSliderCaptcha(String type) {
return CaptchaTypeConstant.SLIDER.equals(type)
|| CaptchaTypeConstant.ROTATE.equals(type)
|| CaptchaTypeConstant.CONCAT.equals(type);
}
/**
* 是否是点击验证码
*
* @param type type
* @return boolean
*/
public static boolean isClickCaptcha(String type) {
return CaptchaTypeConstant.WORD_IMAGE_CLICK.equals(type) || CaptchaTypeConstant.IMAGE_CLICK.equals(type);
}
/**
* 是否是拼图类验证码, 校验的值是 数字排列顺序
*
* @param type 类型
* @return boolean
*/
public static boolean isJigsawCaptcha(String type) {
return CaptchaTypeConstant.JIGSAW.equals(type);
}
}
@@ -268,7 +268,7 @@ public abstract class CollectionUtils {
* @since 5.2.3 * @since 5.2.3
* @see SortedSet * @see SortedSet
* @see LinkedHashMap#keySet() * @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet * @see LinkedHashSet
*/ */
public static <T> T firstElement(Set<T> set) { public static <T> T firstElement(Set<T> set) {
if (isEmpty(set)) { if (isEmpty(set)) {
@@ -307,7 +307,7 @@ public abstract class CollectionUtils {
* @since 5.0.3 * @since 5.0.3
* @see SortedSet * @see SortedSet
* @see LinkedHashMap#keySet() * @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet * @see LinkedHashSet
*/ */
public static <T> T lastElement(Set<T> set) { public static <T> T lastElement(Set<T> set) {
if (isEmpty(set)) { if (isEmpty(set)) {
@@ -2,9 +2,7 @@ package cloud.tianai.captcha.common.util;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.awt.*;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -17,11 +17,7 @@
package cloud.tianai.captcha.common.util; package cloud.tianai.captcha.common.util;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.Arrays; import java.util.*;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
/** /**
* 拷贝spring * 拷贝spring
@@ -60,9 +56,9 @@ public abstract class ObjectUtils {
* that is, neither a RuntimeException nor an Error. * that is, neither a RuntimeException nor an Error.
* @param ex the throwable to check * @param ex the throwable to check
* @return whether the throwable is a checked exception * @return whether the throwable is a checked exception
* @see java.lang.Exception * @see Exception
* @see java.lang.RuntimeException * @see RuntimeException
* @see java.lang.Error * @see Error
*/ */
public static boolean isCheckedException(Throwable ex) { public static boolean isCheckedException(Throwable ex) {
return !(ex instanceof RuntimeException || ex instanceof Error); return !(ex instanceof RuntimeException || ex instanceof Error);
@@ -125,8 +121,8 @@ public abstract class ObjectUtils {
* @since 4.2 * @since 4.2
* @see Optional#isPresent() * @see Optional#isPresent()
* @see ObjectUtils#isEmpty(Object[]) * @see ObjectUtils#isEmpty(Object[])
* @see CollectionUtils#isEmpty(java.util.Collection) * @see CollectionUtils#isEmpty(Collection)
* @see CollectionUtils#isEmpty(java.util.Map) * @see CollectionUtils#isEmpty(Map)
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public static boolean isEmpty(Object obj) { public static boolean isEmpty(Object obj) {
@@ -155,7 +151,7 @@ public abstract class ObjectUtils {
} }
/** /**
* Unwrap the given object which is potentially a {@link java.util.Optional}. * Unwrap the given object which is potentially a {@link Optional}.
* @param obj the candidate object * @param obj the candidate object
* @return either the value held within the {@code Optional}, {@code null} * @return either the value held within the {@code Optional}, {@code null}
* if the {@code Optional} is empty, or simply the given object as-is * if the {@code Optional} is empty, or simply the given object as-is
@@ -308,7 +304,7 @@ public abstract class ObjectUtils {
* @param o2 second Object to compare * @param o2 second Object to compare
* @return whether the given objects are equal * @return whether the given objects are equal
* @see Object#equals(Object) * @see Object#equals(Object)
* @see java.util.Arrays#equals * @see Arrays#equals
*/ */
public static boolean nullSafeEquals(Object o1, Object o2) { public static boolean nullSafeEquals(Object o1, Object o2) {
if (o1 == o2) { if (o1 == o2) {
@@ -333,7 +329,7 @@ public abstract class ObjectUtils {
* @param o2 second array to compare * @param o2 second array to compare
* @return whether the given objects are equal * @return whether the given objects are equal
* @see #nullSafeEquals(Object, Object) * @see #nullSafeEquals(Object, Object)
* @see java.util.Arrays#equals * @see Arrays#equals
*/ */
private static boolean arrayEquals(Object o1, Object o2) { private static boolean arrayEquals(Object o1, Object o2) {
if (o1 instanceof Object[] && o2 instanceof Object[]) { if (o1 instanceof Object[] && o2 instanceof Object[]) {
@@ -2,7 +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.CaptchaTransferData; 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.CustomData;
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;
@@ -20,7 +20,6 @@ import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -102,21 +101,21 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
assertInit(); assertInit();
CustomData data = new CustomData(); CustomData data = new CustomData();
CaptchaTransferData transferData = CaptchaTransferData.create(data, param); CaptchaExchange captchaExchange = CaptchaExchange.create(data, param);
ImageCaptchaInfo imageCaptchaInfo = applyPostProcessorBeforeGenerate(transferData, this); ImageCaptchaInfo imageCaptchaInfo = applyPostProcessorBeforeGenerate(captchaExchange, this);
if (imageCaptchaInfo != null) { if (imageCaptchaInfo != null) {
return imageCaptchaInfo; return imageCaptchaInfo;
} }
doGenerateCaptchaImage(transferData); doGenerateCaptchaImage(captchaExchange);
applyPostProcessorBeforeWrapImageCaptchaInfo(transferData, this); applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
imageCaptchaInfo = wrapImageCaptchaInfo(transferData); imageCaptchaInfo = wrapImageCaptchaInfo(captchaExchange);
applyPostProcessorAfterGenerateCaptchaImage(transferData, imageCaptchaInfo, this); applyPostProcessorAfterGenerateCaptchaImage(captchaExchange, imageCaptchaInfo, this);
return imageCaptchaInfo; return imageCaptchaInfo;
} }
public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaTransferData transferData) { public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(transferData); ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(captchaExchange);
imageCaptchaInfo.setData(transferData.getCustomData()); imageCaptchaInfo.setData(captchaExchange.getCustomData());
return imageCaptchaInfo; return imageCaptchaInfo;
} }
@@ -225,12 +224,12 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
/** /**
* 生成验证码方法 * 生成验证码方法
* *
* @param transferData transferData * @param captchaExchange captchaExchange
* @return ImageCaptchaInfo * @return ImageCaptchaInfo
*/ */
protected abstract void doGenerateCaptchaImage(CaptchaTransferData transferData); protected abstract void doGenerateCaptchaImage(CaptchaExchange captchaExchange);
protected abstract ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData); protected abstract ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange);
@Override @Override
public ImageCaptchaResourceManager getImageResourceManager() { public ImageCaptchaResourceManager getImageResourceManager() {
@@ -41,7 +41,7 @@ public interface ImageCaptchaGenerator {
ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName); ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName);
/** /**
* 生成滑块验证码 * 生成验证码
* *
* @param param 生成参数 * @param param 生成参数
* @return SliderCaptchaInfo * @return SliderCaptchaInfo
@@ -24,5 +24,7 @@ public interface ImageCaptchaGeneratorProvider {
* *
* @return String * @return String
*/ */
String getType(); default String getType() {
return "unknown";
}
} }
@@ -1,7 +1,6 @@
package cloud.tianai.captcha.generator; package cloud.tianai.captcha.generator;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
import cloud.tianai.captcha.generator.common.model.dto.CaptchaTransferData;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
/** /**
@@ -11,15 +10,35 @@ import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
*/ */
public interface ImageCaptchaPostProcessor { public interface ImageCaptchaPostProcessor {
default ImageCaptchaInfo beforeGenerateCaptchaImage(CaptchaTransferData transferData, ImageCaptchaGenerator context) { /**
* 在生成验证码核心逻辑之前调用, 用于拦截验证码生成、限流、自定义返回数据等处理
*
* @param captchaExchange 传输数据
* @param generator generator
* @return ImageCaptchaInfo
*/
default ImageCaptchaInfo beforeGenerateCaptchaImage(CaptchaExchange captchaExchange, ImageCaptchaGenerator generator) {
return null; return null;
} }
default void beforeWrapImageCaptchaInfo(CaptchaTransferData transferData, ImageCaptchaGenerator context) { /**
* 在执行包装 ImageCaptchaInfo 核心逻辑之前处理
*
* @param captchaExchange 传输数据
* @param generator generator
*/
default void beforeWrapImageCaptchaInfo(CaptchaExchange captchaExchange, ImageCaptchaGenerator generator) {
} }
default void afterGenerateCaptchaImage(CaptchaTransferData transferData, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator context) { /**
* 在执行包装 ImageCaptchaInfo 核心逻辑之后处理
*
* @param captchaExchange captchaExchange
* @param imageCaptchaInfo imageCaptchaInfo
* @param generator generator
*/
default void afterGenerateCaptchaImage(CaptchaExchange captchaExchange, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator generator) {
} }
} }
@@ -20,10 +20,11 @@ public interface ImageTransform {
* @param backgroundImage 背景图片 * @param backgroundImage 背景图片
* @param param 参数 * @param param 参数
* @param backgroundResource 背景资源对象 * @param backgroundResource 背景资源对象
* @param data 自定义透传数据
* @return ImageTransformData * @return ImageTransformData
*/ */
default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource,CustomData data) { default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource, CustomData data) {
return transform(param, backgroundImage, null, backgroundResource, null,data); return transform(param, backgroundImage, null, backgroundResource, null, data);
} }
/** /**
@@ -34,6 +35,7 @@ public interface ImageTransform {
* @param param 参数 * @param param 参数
* @param backgroundResource 背景资源对象 * @param backgroundResource 背景资源对象
* @param templateResource 模板资源对象(可能为空) * @param templateResource 模板资源对象(可能为空)
* @param data 自定义透传数据
* @return String * @return String
*/ */
ImageTransformData transform(GenerateParam param, ImageTransformData transform(GenerateParam param,
@@ -0,0 +1,15 @@
package cloud.tianai.captcha.generator.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.awt.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FontWrapper {
private Font font;
private float currentFontTopCoef;
}
@@ -1,18 +0,0 @@
package cloud.tianai.captcha.generator.common.constant;
/**
* @Author: 天爱有情
* @date 2021/8/7 17:14
* @Description 滑块验证码常量
*/
public interface SliderCaptchaConstant {
/** 模板滑块固定名称. */
String TEMPLATE_ACTIVE_IMAGE_NAME = "active.png";
/** 模板凹槽固定名称. */
String TEMPLATE_FIXED_IMAGE_NAME = "fixed.png";
/** 模板蒙版. */
String TEMPLATE_MASK_IMAGE_NAME = "mask.png";
/** 混淆的凹槽.*/
String OBFUSCATE_TEMPLATE_FIXED_IMAGE_NAME = "obfuscate_" + TEMPLATE_FIXED_IMAGE_NAME;
}
@@ -12,24 +12,30 @@ import java.awt.image.BufferedImage;
* @Description 传输用 * @Description 传输用
*/ */
@Data @Data
public class CaptchaTransferData { public class CaptchaExchange {
/** 模板对象. */
private ResourceMap templateResource; private ResourceMap templateResource;
/** 资源对象. */
private Resource resourceImage; private Resource resourceImage;
/** 生成好的背景图片. */
private BufferedImage backgroundImage; private BufferedImage backgroundImage;
/** 生成好的模板图片. */
private BufferedImage templateImage; private BufferedImage templateImage;
/** 最终要回调给验证器的自定义对象. */
private CustomData customData; private CustomData customData;
/** 用户传来的生成参数. */
private GenerateParam param; private GenerateParam param;
/** 传输对象,扩展自定义. */
private Object transferData; private Object transferData;
public static CaptchaTransferData create(CustomData customData, GenerateParam param) { public static CaptchaExchange create(CustomData customData, GenerateParam param) {
CaptchaTransferData captchaTransferData = new CaptchaTransferData(); CaptchaExchange captchaExchange = new CaptchaExchange();
captchaTransferData.setCustomData(customData); captchaExchange.setCustomData(customData);
captchaTransferData.setParam(param); captchaExchange.setParam(param);
return captchaTransferData; return captchaExchange;
} }
public static CaptchaTransferData create(GenerateParam param) { public static CaptchaExchange create(GenerateParam param) {
return create(new CustomData(), param); return create(new CustomData(), param);
} }
} }
@@ -5,6 +5,8 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.awt.*;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
* @date 2022/4/28 16:51 * @date 2022/4/28 16:51
@@ -24,5 +26,7 @@ public class ClickImageCheckDefinition {
private Integer width; private Integer width;
/** 高.*/ /** 高.*/
private Integer height; private Integer height;
/** 颜色.*/
private Color imageColor;
} }
@@ -36,7 +36,6 @@ public class ImageCaptchaInfo {
private Float tolerant; private Float tolerant;
/** 验证码类型. */ /** 验证码类型. */
private String type; private String type;
/** 透传字段,用于传给前端. */
private CustomData data; private CustomData data;
public ImageCaptchaInfo(String backgroundImage, public ImageCaptchaInfo(String backgroundImage,
@@ -9,15 +9,11 @@ import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D; import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber; import java.awt.image.PixelGrabber;
import java.awt.image.RenderedImage;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -75,25 +71,6 @@ public class CaptchaImageUtils {
} }
public static int getMaxPix(BufferedImage image, int x, int y, int width, int height) {
int[] rgbArr = new int[width * height];
image.getRGB(x, y, width, height, rgbArr, 0, width);
Map<Integer, Integer> map = new HashMap<>();
for (int rgb : rgbArr) {
Integer count = map.getOrDefault(rgb, 0);
map.put(rgb, count + 1);
}
AtomicInteger maxRgb = new AtomicInteger();
AtomicInteger maxRgbCount = new AtomicInteger();
map.forEach((r, c) -> {
if (c > maxRgbCount.get()) {
maxRgb.set(r);
maxRgbCount.set(c);
}
});
return maxRgb.get();
}
/** /**
* 将Image图像中的透明/不透明部分转换为Shape图形 * 将Image图像中的透明/不透明部分转换为Shape图形
* *
@@ -195,48 +172,6 @@ public class CaptchaImageUtils {
} }
@SneakyThrows
public static BufferedImage cutImage_bak(BufferedImage origin, BufferedImage template, int x, int y) {
int bw = template.getWidth(null);
int bh = template.getHeight(null);
int lw = origin.getWidth(null);
int lh = origin.getHeight(null);
//得到透明的区域(人物轮廓)
Shape imageShape = getImageShape(template, false);
long end = System.currentTimeMillis();
//合成后的图片
BufferedImage image = new BufferedImage(bw, bh, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
//设置画布为透明
image = graphics.getDeviceConfiguration().createCompatibleImage(bw, bh, Transparency.TRANSLUCENT);
graphics.dispose();
Graphics2D graphics2 = image.createGraphics();
//取交集(限制可以画的范围为shape的范围)
graphics2.clip(imageShape);
//抗锯齿
graphics2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
graphics2.drawImage(origin, -x, -y, lw, lh, null);
graphics2.dispose();
return image;
}
public static void replaceColorByOpaque(BufferedImage oriImage, int replaceRgb) {
int bh = oriImage.getHeight();
int bw = oriImage.getWidth();
// 透明色
for (int y = 0; y < bh; y++) {
for (int x = 0; x < bw; x++) {
int rgb = oriImage.getRGB(x, y);
int alpha = (rgb >> 24) & 0xff;
// 透明度大于100才处理,过滤一下边缘过于透明的像素点
if (alpha > 100) {
oriImage.setRGB(x, y, replaceRgb);
}
}
}
}
/** /**
* 旋转图片 * 旋转图片
* *
@@ -278,26 +213,6 @@ public class CaptchaImageUtils {
} }
/**
* 通过x和y轴截取图片
*
* @param x x
* @param y y
* @param width 宽度
* @param height 高度
* @param img 截取的图片
* @return BufferedImage
*/
public static BufferedImage subImage(int x, int y, int width, int height, BufferedImage img) {
int[] simgRgb = new int[width * height];
img.getRGB(x, y, width, height, simgRgb, 0, width);
// 得到图片透明度。
int type = img.getColorModel().getTransparency();
BufferedImage newImage = new BufferedImage(width, height, type);
newImage.setRGB(0, 0, width, height, simgRgb, 0, width);
return newImage;
}
/** /**
* 分隔图片 * 分隔图片
* *
@@ -354,11 +269,11 @@ public class CaptchaImageUtils {
for (BufferedImage img : imgArr) { for (BufferedImage img : imgArr) {
if (direction) { if (direction) {
// 水平方向 // 水平方向
graphics.drawImage(img, pos, 0, img.getWidth(), img.getHeight(),null); graphics.drawImage(img, pos, 0, img.getWidth(), img.getHeight(), null);
pos += img.getWidth(); pos += img.getWidth();
} else { } else {
// 垂直方向 // 垂直方向
graphics.drawImage(img, 0, pos, img.getWidth(), img.getHeight(),null); graphics.drawImage(img, 0, pos, img.getWidth(), img.getHeight(), null);
pos += img.getHeight(); pos += img.getHeight();
} }
} }
@@ -366,10 +281,6 @@ public class CaptchaImageUtils {
return newImage; return newImage;
} }
public static char getRandomChar() {
return (char) (0x4e00 + (int) (Math.random() * (0x9fa5 - 0x4e00 + 1)));
}
@SneakyThrows @SneakyThrows
public static BufferedImage drawWordImg(Color fontColor, public static BufferedImage drawWordImg(Color fontColor,
@@ -497,23 +408,6 @@ public class CaptchaImageUtils {
} }
public static void drawInterfere(Graphics2D g, int width,
int height,
int interferenceLineNum,
int interferencePointNum) {
ThreadLocalRandom random = ThreadLocalRandom.current();
// 干扰点
if (interferencePointNum > 0) {
drawOval(interferencePointNum, null, g, width, height, random);
}
if (interferencePointNum > 0) {
g.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
// 干扰线
drawBesselLine(interferenceLineNum, null, g, width, height, random);
}
}
/** /**
* 随机获取颜色 * 随机获取颜色
* *
@@ -527,12 +421,6 @@ public class CaptchaImageUtils {
} }
public static RenderedImage toRenderedImage(Image img) {
if (img instanceof RenderedImage) {
return (RenderedImage) img;
}
return copyImage(img, BufferedImage.TYPE_INT_RGB);
}
/** /**
* 转换成指定类型的 BufferedImage * 转换成指定类型的 BufferedImage
@@ -595,30 +483,6 @@ public class CaptchaImageUtils {
return bimage; return bimage;
} }
/**
* 灰度处理,把原图传进去,传出来为修改后的图
*
* @param b b
* @return BufferedImage
*/
public static BufferedImage gray(BufferedImage b) {
int width = b.getWidth();
int height = b.getHeight();
// 下面这个别忘了定义,不然会出错
BufferedImage bufferedImageEnd = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
// 双层循环更改图片的RGB值,把得到的灰度值存到bufferedImage_end中,然后返回bufferedImage_end
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 获取到(x,y)此像素点的Colo,转化为灰度
Color color = new Color(b.getRGB(x, y));
int gray = (int) (color.getRed() * 0.299 + color.getGreen() * 0.587 + color.getBlue() * 0.114);
Color color_end = new Color(gray, gray, gray);
bufferedImageEnd.setRGB(x, y, color_end.getRGB());
}
}
return bufferedImageEnd;
}
/** /**
* 创建画板 * 创建画板
* *
@@ -636,21 +500,6 @@ public class CaptchaImageUtils {
return g; return g;
} }
public static BufferedImage toBufferedImage(Image img) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
/** /**
* 后缀是否是jpg * 后缀是否是jpg
* *
@@ -672,3 +521,4 @@ public class CaptchaImageUtils {
} }
} }
@@ -1,10 +1,9 @@
package cloud.tianai.captcha.generator.impl; package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator; import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.common.model.dto.CaptchaTransferData; import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
import cloud.tianai.captcha.generator.common.model.dto.ClickImageCheckDefinition; import cloud.tianai.captcha.generator.common.model.dto.ClickImageCheckDefinition;
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.util.CaptchaImageUtils; import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
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;
@@ -13,10 +12,10 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -25,6 +24,7 @@ import java.util.concurrent.ThreadLocalRandom;
*/ */
public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator { public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
public static final String CLICK_IMAGE_DISTORT_KEY = "clickImageDistort";
public AbstractClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) { public AbstractClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager); super(imageCaptchaResourceManager);
@@ -35,8 +35,8 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
@SneakyThrows @SneakyThrows
@Override @Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) { public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
// 文字点选验证码不需要模板 只需要背景图 // 文字点选验证码不需要模板 只需要背景图
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
@@ -65,19 +65,20 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
// 随机y // 随机y
int randomY = randomInt(10, bgImage.getHeight() - clickImgHeight); int randomY = randomInt(10, bgImage.getHeight() - clickImgHeight);
// 通过随机x和y 进行覆盖图片 // 通过随机x和y 进行覆盖图片
CaptchaImageUtils.overlayImage(bgImage, imgWrapper.getImage(), randomX, randomY); CaptchaImageUtils.overlayImage(bgImage, image, randomX, randomY);
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);
clickImageCheckDefinition.setY(randomY + clickImgHeight / 2); clickImageCheckDefinition.setY(randomY + clickImgHeight / 2);
clickImageCheckDefinition.setWidth(clickImgWidth); clickImageCheckDefinition.setWidth(clickImgWidth);
clickImageCheckDefinition.setHeight(clickImgHeight); clickImageCheckDefinition.setHeight(clickImgHeight);
clickImageCheckDefinition.setImageColor(imgWrapper.getImageColor());
clickImageCheckDefinitionList.add(clickImageCheckDefinition); clickImageCheckDefinitionList.add(clickImageCheckDefinition);
} }
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList); List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList);
transferData.setBackgroundImage(bgImage); captchaExchange.setBackgroundImage(bgImage);
transferData.setTransferData(checkClickImageCheckDefinitionList); captchaExchange.setTransferData(checkClickImageCheckDefinitionList);
transferData.setResourceImage(resourceImage); captchaExchange.setResourceImage(resourceImage);
// // wrap // // wrap
@@ -123,5 +124,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
private BufferedImage image; private BufferedImage image;
/** 提示. */ /** 提示. */
private Resource tip; private Resource tip;
/** 图片颜色. */
private Color imageColor;
} }
} }
@@ -1,18 +1,14 @@
package cloud.tianai.captcha.generator.impl; package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.util.ObjectUtils; import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator; 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.model.dto.CaptchaTransferData; 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;
import cloud.tianai.captcha.generator.impl.provider.StandardConcatImageCaptchaGeneratorProvider; import cloud.tianai.captcha.generator.impl.provider.CommonImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.impl.provider.StandardWordClickImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.impl.provider.StandardRotateImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.impl.provider.StandardSliderImageImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -21,6 +17,8 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static cloud.tianai.captcha.common.constant.CaptchaTypeConstant.*;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
* @date 2022/4/24 9:27 * @date 2022/4/24 9:27
@@ -33,7 +31,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Setter @Setter
@Getter @Getter
private String defaultCaptcha = CaptchaTypeConstant.SLIDER; private String defaultCaptcha = SLIDER;
protected boolean initDefaultResource = false; protected boolean initDefaultResource = false;
@@ -49,14 +47,15 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Override @Override
protected void doInit(boolean initDefaultResource) { protected void doInit(boolean initDefaultResource) {
this.initDefaultResource = initDefaultResource; this.initDefaultResource = initDefaultResource;
// 滑块验证码 // 滑块验证码
addImageCaptchaGeneratorProvider(new StandardSliderImageImageCaptchaGeneratorProvider()); addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(SLIDER, StandardSliderImageCaptchaGenerator::new));
// 旋转验证码 // 旋转验证码
addImageCaptchaGeneratorProvider(new StandardRotateImageCaptchaGeneratorProvider()); addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(ROTATE, StandardRotateImageCaptchaGenerator::new));
// 拼接验证码 // 拼接验证码
addImageCaptchaGeneratorProvider(new StandardConcatImageCaptchaGeneratorProvider()); addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new));
// 点选文字验证码 // 点选文字验证码
addImageCaptchaGeneratorProvider(new StandardWordClickImageCaptchaGeneratorProvider()); addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, StandardWordClickImageCaptchaGenerator::new));
} }
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) { public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
@@ -96,12 +95,12 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Override @Override
protected void doGenerateCaptchaImage(CaptchaTransferData transferData) { protected void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
} }
@Override @Override
protected ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) { protected ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
return null; return null;
} }
@@ -11,15 +11,11 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG; import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.*; import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.concatImage;
import static cloud.tianai.captcha.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH; import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.splitImage;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -51,8 +47,8 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
} }
@Override @Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) { public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
// 拼接验证码不需要模板 只需要背景图 // 拼接验证码不需要模板 只需要背景图
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
BufferedImage bgImage = getResourceImage(resourceImage); BufferedImage bgImage = getResourceImage(resourceImage);
@@ -72,9 +68,9 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
data.x = randomX; data.x = randomX;
data.y = randomY; data.y = randomY;
transferData.setTransferData(data); captchaExchange.setTransferData(data);
transferData.setBackgroundImage(bgImage); captchaExchange.setBackgroundImage(bgImage);
transferData.setResourceImage(resourceImage); captchaExchange.setResourceImage(resourceImage);
} }
public static class Data { public static class Data {
@@ -84,13 +80,13 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows @SneakyThrows
@Override @Override
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) { public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
BufferedImage bgImage = transferData.getBackgroundImage(); BufferedImage bgImage = captchaExchange.getBackgroundImage();
Resource resourceImage = transferData.getResourceImage(); Resource resourceImage = captchaExchange.getResourceImage();
CustomData customData = transferData.getCustomData(); CustomData customData = captchaExchange.getCustomData();
ImageTransformData transform = getImageTransform().transform(param, bgImage, resourceImage, customData); ImageTransformData transform = getImageTransform().transform(param, bgImage, resourceImage, customData);
Data data = (Data) transferData.getTransferData(); Data data = (Data) captchaExchange.getTransferData();
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(transform.getBackgroundImageUrl(), ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(transform.getBackgroundImageUrl(),
null, null,
resourceImage.getTag(), resourceImage.getTag(),
@@ -4,7 +4,6 @@ import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CommonConstant; import cloud.tianai.captcha.common.constant.CommonConstant;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator; import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageTransform; import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant;
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.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
@@ -15,14 +14,9 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.util.Optional;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG; import static cloud.tianai.captcha.common.constant.CommonConstant.*;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.*;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.TEMPLATE_MASK_IMAGE_NAME;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -31,6 +25,13 @@ import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConsta
*/ */
public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGenerator { public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/** 模板滑块固定名称. */
public static String TEMPLATE_ACTIVE_IMAGE_NAME = "active.png";
/** 模板凹槽固定名称. */
public static String TEMPLATE_FIXED_IMAGE_NAME = "fixed.png";
/** 模板蒙版. */
public static String TEMPLATE_MASK_IMAGE_NAME = "mask.png";
public StandardRotateImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) { public StandardRotateImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager); super(imageCaptchaResourceManager);
} }
@@ -50,18 +51,24 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
public void initDefaultResource() { public void initDefaultResource() {
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
// 添加一些系统的资源文件 // 添加一些系统的资源文件
resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG)); resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
// 添加一些系统的 模板文件 // 添加一些系统的 模板文件
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4); ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png"))); template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png")));
template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png"))); template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1); resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1);
ResourceMap template2 = new ResourceMap(CommonConstant.DEFAULT_TAG + "_obfuscate", 4);
template2.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/4/active.png")));
template2.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/4/fixed.png")));
template2.put(TEMPLATE_MASK_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/4/mask.png")));
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template2);
} }
@Override @Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) { public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
CustomData data = new CustomData(); CustomData data = new CustomData();
ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag()); ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
@@ -96,17 +103,17 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
int randomX = randomInt(fixedTemplate.getWidth() + 10, background.getWidth() - 10); int randomX = randomInt(fixedTemplate.getWidth() + 10, background.getWidth() - 10);
double degree = 360d - randomX / ((background.getWidth()) / 360d); double degree = 360d - randomX / ((background.getWidth()) / 360d);
// 旋转的透明图片是一张正方形的 // 旋转的透明图片是一张正方形的
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(background.getHeight(), background.getHeight()); BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(cutImage.getWidth(), background.getHeight());
CaptchaImageUtils.centerOverlayAndRotateImage(matrixTemplate, cutImage, degree); CaptchaImageUtils.centerOverlayAndRotateImage(matrixTemplate, cutImage, degree);
RotateData rotateData = new RotateData(); RotateData rotateData = new RotateData();
rotateData.degree = degree; rotateData.degree = degree;
rotateData.randomX = randomX; rotateData.randomX = randomX;
transferData.setTransferData(rotateData); captchaExchange.setTransferData(rotateData);
transferData.setBackgroundImage(background); captchaExchange.setBackgroundImage(background);
transferData.setTemplateImage(matrixTemplate); captchaExchange.setTemplateImage(matrixTemplate);
transferData.setTemplateResource(templateResource); captchaExchange.setTemplateResource(templateResource);
transferData.setResourceImage(resourceImage); captchaExchange.setResourceImage(resourceImage);
// return wrapRotateCaptchaInfo(degree, randomX, background, matrixTemplate, param, templateResource, resourceImage, data); // return wrapRotateCaptchaInfo(degree, randomX, background, matrixTemplate, param, templateResource, resourceImage, data);
} }
@@ -125,14 +132,14 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows @SneakyThrows
@Override @Override
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) { public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
BufferedImage backgroundImage = transferData.getBackgroundImage(); BufferedImage backgroundImage = captchaExchange.getBackgroundImage();
BufferedImage sliderImage = transferData.getTemplateImage(); BufferedImage sliderImage = captchaExchange.getTemplateImage();
Resource resourceImage = transferData.getResourceImage(); Resource resourceImage = captchaExchange.getResourceImage();
ResourceMap templateResource = transferData.getTemplateResource(); ResourceMap templateResource = captchaExchange.getTemplateResource();
CustomData data = transferData.getCustomData(); CustomData data = captchaExchange.getCustomData();
RotateData rotateData = (RotateData) transferData.getTransferData(); RotateData rotateData = (RotateData) captchaExchange.getTransferData();
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, data); ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, data);
RotateImageCaptchaInfo imageCaptchaInfo = RotateImageCaptchaInfo.of(rotateData.degree, RotateImageCaptchaInfo imageCaptchaInfo = RotateImageCaptchaInfo.of(rotateData.degree,
rotateData.randomX, rotateData.randomX,
@@ -3,7 +3,6 @@ package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator; import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageTransform; import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant;
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.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
@@ -16,14 +15,10 @@ import lombok.extern.slf4j.Slf4j;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.util.Optional;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG; import static cloud.tianai.captcha.common.constant.CommonConstant.*;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.*;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -33,14 +28,16 @@ import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConsta
@Slf4j @Slf4j
public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGenerator { public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/**
* 默认的resource资源文件路径. /** 模板滑块固定名称. */
*/ public static String TEMPLATE_ACTIVE_IMAGE_NAME = "active.png";
public static final String DEFAULT_SLIDER_IMAGE_RESOURCE_PATH = "META-INF/cut-image/resource"; /** 模板凹槽固定名称. */
/** public static String TEMPLATE_FIXED_IMAGE_NAME = "fixed.png";
* 默认的template资源文件路径. /** 模板蒙版. */
*/ public static String TEMPLATE_MASK_IMAGE_NAME = "mask.png";
public static final String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template"; /** 混淆的凹槽. */
public static String OBFUSCATE_TEMPLATE_FIXED_IMAGE_NAME = "obfuscate_" + TEMPLATE_FIXED_IMAGE_NAME;
public StandardSliderImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) { public StandardSliderImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager); super(imageCaptchaResourceManager);
@@ -60,8 +57,8 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows @SneakyThrows
@Override @Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) { public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
Boolean obfuscate = param.getObfuscate(); Boolean obfuscate = param.getObfuscate();
ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag()); ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
@@ -93,15 +90,15 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
XandY xandY = new XandY(); XandY xandY = new XandY();
xandY.x = randomX; xandY.x = randomX;
xandY.y = randomY; xandY.y = randomY;
transferData.setBackgroundImage(background); captchaExchange.setBackgroundImage(background);
transferData.setTemplateImage(matrixTemplate); captchaExchange.setTemplateImage(matrixTemplate);
transferData.setTemplateResource(templateResource); captchaExchange.setTemplateResource(templateResource);
transferData.setResourceImage(resourceImage); captchaExchange.setResourceImage(resourceImage);
transferData.setTransferData(xandY); captchaExchange.setTransferData(xandY);
// 后处理 // 后处理
// applyPostProcessorBeforeWrapImageCaptchaInfo(transferData, this); // applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, transferData); // imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, captchaExchange);
// applyPostProcessorAfterGenerateCaptchaImage(transferData, imageCaptchaInfo, this); // applyPostProcessorAfterGenerateCaptchaImage(captchaExchange, imageCaptchaInfo, this);
// return imageCaptchaInfo; // return imageCaptchaInfo;
} }
@@ -134,14 +131,14 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows @SneakyThrows
@Override @Override
public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) { public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
BufferedImage backgroundImage = transferData.getBackgroundImage(); BufferedImage backgroundImage = captchaExchange.getBackgroundImage();
BufferedImage sliderImage = transferData.getTemplateImage(); BufferedImage sliderImage = captchaExchange.getTemplateImage();
Resource resourceImage = transferData.getResourceImage(); Resource resourceImage = captchaExchange.getResourceImage();
ResourceMap templateResource = transferData.getTemplateResource(); ResourceMap templateResource = captchaExchange.getTemplateResource();
CustomData customData = transferData.getCustomData(); CustomData customData = captchaExchange.getCustomData();
XandY data = (XandY) transferData.getTransferData(); XandY data = (XandY) captchaExchange.getTransferData();
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData); ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData);
SliderImageCaptchaInfo imageCaptchaInfo = SliderImageCaptchaInfo.of(data.x, data.y, SliderImageCaptchaInfo imageCaptchaInfo = SliderImageCaptchaInfo.of(data.x, data.y,
@@ -4,13 +4,16 @@ import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.util.CollectionUtils; 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.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.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore; import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider; import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.*; import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@@ -22,6 +25,7 @@ import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG; import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
/** /**
@@ -114,7 +118,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
public void initDefaultResource() { public void initDefaultResource() {
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
// 添加一些系统的资源文件 // 添加一些系统的资源文件
resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG)); resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
} }
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) { public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
@@ -130,7 +134,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
float top = 6 / 2f + font.getSize() - currentFontTopCoef; float top = 6 / 2f + font.getSize() - currentFontTopCoef;
BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips, BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips,
font, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum); font, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum);
return new ImgWrapper(bufferedImage, new Resource(null, tips)); return new ImgWrapper(bufferedImage, new Resource(null, tips), null);
} }
@Override @Override
@@ -150,7 +154,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
clickImgWidth, clickImgWidth,
clickImgHeight, clickImgHeight,
randomDeg); randomDeg);
return new ImgWrapper(fontImage, tip); return new ImgWrapper(fontImage, tip, randomColor);
} }
@Override @Override
@@ -167,12 +171,12 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
} }
@Override @Override
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) { public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = (List<ClickImageCheckDefinition>) transferData.getTransferData(); List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = (List<ClickImageCheckDefinition>) captchaExchange.getTransferData();
BufferedImage bgImage = transferData.getBackgroundImage(); BufferedImage bgImage = captchaExchange.getBackgroundImage();
GenerateParam param = transferData.getParam(); GenerateParam param = captchaExchange.getParam();
Resource resourceImage = transferData.getResourceImage(); Resource resourceImage = captchaExchange.getResourceImage();
CustomData data = transferData.getCustomData(); CustomData data = captchaExchange.getCustomData();
// 提示图片 // 提示图片
BufferedImage tipImage = genTipImage(checkClickImageCheckDefinitionList).getImage(); BufferedImage tipImage = genTipImage(checkClickImageCheckDefinitionList).getImage();
ImageTransformData transform = getImageTransform().transform(param, bgImage, tipImage, resourceImage, checkClickImageCheckDefinitionList, data); ImageTransformData transform = getImageTransform().transform(param, bgImage, tipImage, resourceImage, checkClickImageCheckDefinitionList, data);
@@ -191,12 +195,5 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
return clickImageCaptchaInfo; return clickImageCaptchaInfo;
} }
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class FontWrapper {
private Font font;
private float currentFontTopCoef;
}
}
}
@@ -3,7 +3,7 @@ package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.exception.ImageCaptchaException; import cloud.tianai.captcha.common.exception.ImageCaptchaException;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaPostProcessor; import cloud.tianai.captcha.generator.ImageCaptchaPostProcessor;
import cloud.tianai.captcha.generator.common.model.dto.CaptchaTransferData; import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import lombok.Getter; import lombok.Getter;
@@ -20,10 +20,6 @@ public class StaticCaptchaPostProcessorManager {
@Getter @Getter
private static LinkedList<ImageCaptchaPostProcessor> processors = new LinkedList<>(); private static LinkedList<ImageCaptchaPostProcessor> processors = new LinkedList<>();
static {
}
public static void add(ImageCaptchaPostProcessor processor) { public static void add(ImageCaptchaPostProcessor processor) {
processors.add(processor); processors.add(processor);
} }
@@ -49,10 +45,10 @@ public class StaticCaptchaPostProcessorManager {
} }
public static ImageCaptchaInfo applyPostProcessorBeforeGenerate(CaptchaTransferData transferData, ImageCaptchaGenerator context) { public static ImageCaptchaInfo applyPostProcessorBeforeGenerate(CaptchaExchange captchaExchange, ImageCaptchaGenerator context) {
for (ImageCaptchaPostProcessor processor : processors) { for (ImageCaptchaPostProcessor processor : processors) {
try { try {
ImageCaptchaInfo imageCaptchaInfo = processor.beforeGenerateCaptchaImage(transferData, context); ImageCaptchaInfo imageCaptchaInfo = processor.beforeGenerateCaptchaImage(captchaExchange, context);
if (imageCaptchaInfo != null) { if (imageCaptchaInfo != null) {
return imageCaptchaInfo; return imageCaptchaInfo;
} }
@@ -63,10 +59,10 @@ public class StaticCaptchaPostProcessorManager {
return null; return null;
} }
public static void applyPostProcessorBeforeWrapImageCaptchaInfo(CaptchaTransferData transferData, ImageCaptchaGenerator context) { public static void applyPostProcessorBeforeWrapImageCaptchaInfo(CaptchaExchange captchaExchange, ImageCaptchaGenerator context) {
for (ImageCaptchaPostProcessor processor : processors) { for (ImageCaptchaPostProcessor processor : processors) {
try { try {
processor.beforeWrapImageCaptchaInfo(transferData, context); processor.beforeWrapImageCaptchaInfo(captchaExchange, context);
} catch (Exception e) { } catch (Exception e) {
throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.beforeWrapImageCaptchaInfo error, [" + processor.getClass() + "]", e); throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.beforeWrapImageCaptchaInfo error, [" + processor.getClass() + "]", e);
} }
@@ -74,10 +70,10 @@ public class StaticCaptchaPostProcessorManager {
} }
public static void applyPostProcessorAfterGenerateCaptchaImage(CaptchaTransferData transferData, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator context) { public static void applyPostProcessorAfterGenerateCaptchaImage(CaptchaExchange captchaExchange, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator context) {
for (ImageCaptchaPostProcessor processor : processors) { for (ImageCaptchaPostProcessor processor : processors) {
try { try {
processor.afterGenerateCaptchaImage(transferData, imageCaptchaInfo, context); processor.afterGenerateCaptchaImage(captchaExchange, imageCaptchaInfo, context);
} catch (Exception e) { } catch (Exception e) {
throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.afterGenerateCaptchaImage error, [" + processor.getClass() + "]", e); throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.afterGenerateCaptchaImage error, [" + processor.getClass() + "]", e);
} }
@@ -1,20 +1,28 @@
package cloud.tianai.captcha.generator.impl.provider; package cloud.tianai.captcha.generator.impl.provider;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
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.impl.StandardWordClickImageCaptchaGenerator;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
public class StandardWordClickImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider { public class CommonImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider {
private String type;
private ImageCaptchaGeneratorProvider provider;
public CommonImageCaptchaGeneratorProvider(String type, ImageCaptchaGeneratorProvider provider) {
this.type = type;
this.provider = provider;
}
@Override @Override
public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) { public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) {
return new StandardWordClickImageCaptchaGenerator(resourceManager, imageTransform); return provider.get(resourceManager, imageTransform);
} }
@Override @Override
public String getType() { public String getType() {
return CaptchaTypeConstant.WORD_IMAGE_CLICK; return type;
} }
} }
@@ -1,25 +0,0 @@
package cloud.tianai.captcha.generator.impl.provider;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.impl.StandardConcatImageCaptchaGenerator;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
/**
* @Author: 天爱有情
* @date 2022/5/19 15:12
* @Description 滑动还原验证码
*/
public class StandardConcatImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider {
@Override
public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) {
return new StandardConcatImageCaptchaGenerator(resourceManager, imageTransform);
}
@Override
public String getType() {
return CaptchaTypeConstant.CONCAT;
}
}
@@ -1,25 +0,0 @@
package cloud.tianai.captcha.generator.impl.provider;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.impl.StandardRotateImageCaptchaGenerator;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
/**
* @Author: 天爱有情
* @date 2022/5/19 15:14
* @Description 旋转验证码
*/
public class StandardRotateImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider {
@Override
public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) {
return new StandardRotateImageCaptchaGenerator(resourceManager, imageTransform);
}
@Override
public String getType() {
return CaptchaTypeConstant.ROTATE;
}
}
@@ -1,25 +0,0 @@
package cloud.tianai.captcha.generator.impl.provider;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.impl.StandardSliderImageCaptchaGenerator;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
/**
* @Author: 天爱有情
* @date 2022/5/19 15:09
* @Description 滑动验证码 生成器
*/
public class StandardSliderImageImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider {
@Override
public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) {
return new StandardSliderImageCaptchaGenerator(resourceManager, imageTransform);
}
@Override
public String getType() {
return CaptchaTypeConstant.SLIDER;
}
}
@@ -6,7 +6,6 @@ import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData; import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils; import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.generator.common.util.ImgWriter; import cloud.tianai.captcha.generator.common.util.ImgWriter;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -60,12 +59,7 @@ public class Base64ImageTransform implements ImageTransform {
} }
@Override @Override
public ImageTransformData transform(GenerateParam param, public ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, BufferedImage templateImage, Object backgroundResource, Object templateResource, CustomData data) {
BufferedImage backgroundImage,
BufferedImage templateImage,
Object backgroundResource,
Object templateResource,
CustomData data) {
ImageTransformData imageTransformData = new ImageTransformData(); ImageTransformData imageTransformData = new ImageTransformData();
if (backgroundImage != null) { if (backgroundImage != null) {
imageTransformData.setBackgroundImageUrl(transform(backgroundImage, param.getBackgroundFormatName())); imageTransformData.setBackgroundImageUrl(transform(backgroundImage, param.getBackgroundFormatName()));
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.resource; package cloud.tianai.captcha.resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
import java.io.InputStream; import java.io.InputStream;
@@ -3,8 +3,6 @@ package cloud.tianai.captcha.resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap; import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import java.util.Map;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
* @date 2022/5/7 9:04 * @date 2022/5/7 9:04
@@ -1,7 +1,6 @@
package cloud.tianai.captcha.resource.common.model.dto; package cloud.tianai.captcha.resource.common.model.dto;
import cloud.tianai.captcha.resource.ResourceProvider; import cloud.tianai.captcha.resource.ResourceProvider;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -19,7 +18,10 @@ public class Resource {
public String data; public String data;
/** 标签.*/ /** 标签.*/
private String tag; private String tag;
/** 提示.*/
private String tip;
/** 扩展.*/
private Object extra;
public Resource(String type, String data) { public Resource(String type, String data) {
this.type = type; this.type = type;
this.data = data; this.data = data;
@@ -30,4 +32,11 @@ public class Resource {
this.data = data; this.data = data;
this.tag = tag; this.tag = tag;
} }
public Resource(String type, String data, String tag, String tip) {
this.type = type;
this.data = data;
this.tag = tag;
this.tip = tip;
}
} }
@@ -1,12 +1,10 @@
package cloud.tianai.captcha.resource.impl; package cloud.tianai.captcha.resource.impl;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceProvider; import cloud.tianai.captcha.resource.ResourceProvider;
import cloud.tianai.captcha.resource.ResourceStore; import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider; import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import cloud.tianai.captcha.resource.impl.provider.FileResourceProvider; import cloud.tianai.captcha.resource.impl.provider.FileResourceProvider;
import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider; import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider;
@@ -15,7 +13,6 @@ import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -41,7 +38,7 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
private void init() { private void init() {
if (this.resourceStore == null) { if (this.resourceStore == null) {
this.resourceStore = new DefaultResourceStore(); this.resourceStore = new LocalMemoryResourceStore();
} }
// 注入一些默认的提供者 // 注入一些默认的提供者
registerResourceProvider(new URLResourceProvider()); registerResourceProvider(new URLResourceProvider());
@@ -1,5 +1,6 @@
package cloud.tianai.captcha.resource.impl; package cloud.tianai.captcha.resource.impl;
import cloud.tianai.captcha.common.constant.CommonConstant;
import cloud.tianai.captcha.common.util.CollectionUtils; import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.ObjectUtils; import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.resource.ResourceStore; import cloud.tianai.captcha.resource.ResourceStore;
@@ -14,17 +15,8 @@ import java.util.concurrent.ThreadLocalRandom;
* @date 2021/8/7 15:43 * @date 2021/8/7 15:43
* @Description 默认的资源存储 * @Description 默认的资源存储
*/ */
public class DefaultResourceStore implements ResourceStore { public class LocalMemoryResourceStore implements ResourceStore {
private static final String TYPE_TAG_SPLIT_FLAG = "|"; private static final String TYPE_TAG_SPLIT_FLAG = "|";
/**
* 模板资源.
*/
private Map<String, List<ResourceMap>> templateResourceMap = new HashMap<>(2);
/**
* resource.
*/
private Map<String, List<Resource>> resourceMap = new HashMap<>(2);
/** 用于检索 type和tag. */ /** 用于检索 type和tag. */
private Map<String, List<ResourceMap>> templateResourceTagMap = new HashMap<>(2); private Map<String, List<ResourceMap>> templateResourceTagMap = new HashMap<>(2);
@@ -32,32 +24,25 @@ public class DefaultResourceStore implements ResourceStore {
@Override @Override
public void addResource(String type, Resource resource) { public void addResource(String type, Resource resource) {
resourceMap.computeIfAbsent(type, k -> new ArrayList<>(20)).add(resource); if (ObjectUtils.isEmpty(resource.getTag())) {
// 添加tag标签字典 resource.setTag(CommonConstant.DEFAULT_TAG);
if (!ObjectUtils.isEmpty(resource.getTag())) {
resourceTagMap.computeIfAbsent(mergeTypeAndTag(type, resource.getTag()), k -> new ArrayList<>(20)).add(resource);
} }
resourceTagMap.computeIfAbsent(mergeTypeAndTag(type, resource.getTag()), k -> new ArrayList<>(20)).add(resource);
} }
@Override @Override
public void addTemplate(String type, ResourceMap template) { public void addTemplate(String type, ResourceMap template) {
templateResourceMap.computeIfAbsent(type, k -> new ArrayList<>(2)).add(template); if (ObjectUtils.isEmpty(template.getTag())) {
// 添加tag标签字典 template.setTag(CommonConstant.DEFAULT_TAG);
if (!ObjectUtils.isEmpty(template.getTag())) {
templateResourceTagMap.computeIfAbsent(mergeTypeAndTag(type, template.getTag()), k -> new ArrayList<>(2)).add(template);
} }
templateResourceTagMap.computeIfAbsent(mergeTypeAndTag(type, template.getTag()), k -> new ArrayList<>(2)).add(template);
} }
@Override @Override
public Resource randomGetResourceByTypeAndTag(String type, String tag) { public Resource randomGetResourceByTypeAndTag(String type, String tag) {
List<Resource> resources; List<Resource> resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
if (ObjectUtils.isEmpty(tag)) {
resources = resourceMap.get(type);
} else {
resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
}
if (CollectionUtils.isEmpty(resources)) { if (CollectionUtils.isEmpty(resources)) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type); throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
} }
if (resources.size() == 1) { if (resources.size() == 1) {
return resources.get(0); return resources.get(0);
@@ -68,16 +53,10 @@ public class DefaultResourceStore implements ResourceStore {
@Override @Override
public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) { public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) {
List<ResourceMap> templateList; List<ResourceMap> templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
if (ObjectUtils.isEmpty(tag)) {
templateList = templateResourceMap.get(type);
} else {
templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
}
if (CollectionUtils.isEmpty(templateList)) { if (CollectionUtils.isEmpty(templateList)) {
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type); throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
} }
if (templateList.size() == 1) { if (templateList.size() == 1) {
return templateList.get(0); return templateList.get(0);
} }
@@ -86,53 +65,56 @@ public class DefaultResourceStore implements ResourceStore {
} }
public String mergeTypeAndTag(String type, String tag) { public String mergeTypeAndTag(String type, String tag) {
if (tag == null) {
tag = CommonConstant.DEFAULT_TAG;
}
return type + TYPE_TAG_SPLIT_FLAG + tag; return type + TYPE_TAG_SPLIT_FLAG + tag;
} }
public void clearResources(String type) { public void clearResources(String type, String tag) {
resourceMap.remove(type); resourceTagMap.remove(mergeTypeAndTag(type, tag));
} }
public void clearAllResources() { public void clearAllResources() {
resourceMap.clear(); resourceTagMap.clear();
} }
public Map<String, List<Resource>> listAllResources() { public Map<String, List<Resource>> listAllResources() {
return resourceMap; return resourceTagMap;
} }
public List<Resource> listResourcesByType(String type) { public List<Resource> listResourcesByType(String type, String tag) {
return resourceMap.getOrDefault(type, Collections.emptyList()); return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
} }
public int getAllResourceCount() { public int getAllResourceCount() {
int count = 0; int count = 0;
for (List<Resource> value : resourceMap.values()) { for (List<Resource> value : resourceTagMap.values()) {
count += value.size(); count += value.size();
} }
return count; return count;
} }
public int getResourceCount(String type) { public int getResourceCount(String type, String tag) {
return resourceMap.getOrDefault(type, Collections.emptyList()).size(); return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList()).size();
} }
public void clearAllTemplates() { public void clearAllTemplates() {
templateResourceMap.clear(); templateResourceTagMap.clear();
} }
public void clearTemplates(String type) { public void clearTemplates(String type, String tag) {
templateResourceMap.remove(type); templateResourceTagMap.remove(mergeTypeAndTag(type, tag));
} }
public List<ResourceMap> listTemplatesByType(String type) { public List<ResourceMap> listTemplatesByType(String type, String tag) {
return templateResourceMap.getOrDefault(type, Collections.emptyList()); return templateResourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
} }
public Map<String, List<ResourceMap>> listAllTemplates() { public Map<String, List<ResourceMap>> listAllTemplates() {
return templateResourceMap; return templateResourceTagMap;
} }
@@ -1,7 +1,5 @@
package cloud.tianai.captcha.resource.impl.provider; package cloud.tianai.captcha.resource.impl.provider;
import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.AbstractResourceProvider; import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
@@ -1,7 +1,5 @@
package cloud.tianai.captcha.resource.impl.provider; package cloud.tianai.captcha.resource.impl.provider;
import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.AbstractResourceProvider; import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.resource.impl.provider; package cloud.tianai.captcha.resource.impl.provider;
import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.AbstractResourceProvider; import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@@ -20,10 +20,10 @@ public class ImageCaptchaTrack {
private Integer bgImageWidth; private Integer bgImageWidth;
/** 背景图片高度. */ /** 背景图片高度. */
private Integer bgImageHeight; private Integer bgImageHeight;
/** 滑块图片宽度. */ /** 模板图片宽度. */
private Integer sliderImageWidth; private Integer templateImageWidth;
/** 滑块图片高度. */ /** 模板图片高度. */
private Integer sliderImageHeight; private Integer templateImageHeight;
/** 滑动开始时间. */ /** 滑动开始时间. */
private Date startSlidingTime; private Date startSlidingTime;
/** 滑动结束时间. */ /** 滑动结束时间. */
@@ -32,28 +32,16 @@ public class ImageCaptchaTrack {
private List<Track> trackList; private List<Track> trackList;
/** 扩展数据,用户传输加密数据等.*/ /** 扩展数据,用户传输加密数据等.*/
private Object data; private Object data;
/**
* 兼容一下旧版本
*
* @return Date
*/
public Date getEntSlidingTime() {
return endSlidingTime;
}
public void setEntSlidingTime(Date entSlidingTime) {
this.endSlidingTime = entSlidingTime;
}
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class Track { public static class Track {
/** x. */ /** x. */
private Integer x; private Float x;
/** y. */ /** y. */
private Integer y; private Float y;
/** 时间. */ /** 时间. */
private Integer t; private Float t;
/** 类型. */ /** 类型. */
private String type = TrackTypeConstant.MOVE; private String type = TrackTypeConstant.MOVE;
} }
@@ -2,13 +2,9 @@ package cloud.tianai.captcha.validator.impl;
import cloud.tianai.captcha.common.response.ApiResponse; import cloud.tianai.captcha.common.response.ApiResponse;
import cloud.tianai.captcha.common.response.CodeDefinition; import cloud.tianai.captcha.common.response.CodeDefinition;
import cloud.tianai.captcha.common.util.CaptchaUtils; import cloud.tianai.captcha.common.util.CaptchaTypeClassifier;
import cloud.tianai.captcha.common.util.CollectionUtils; import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.ObjectUtils; import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.common.util.CaptchaUtils;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack; import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
import java.util.List; import java.util.List;
@@ -21,6 +17,7 @@ import java.util.Map;
*/ */
public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator { public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
public static final CodeDefinition DEFINITION = new CodeDefinition(50001, "basic check fail"); public static final CodeDefinition DEFINITION = new CodeDefinition(50001, "basic check fail");
public BasicCaptchaTrackValidator() { public BasicCaptchaTrackValidator() {
} }
@@ -29,15 +26,15 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
} }
@Override @Override
public ApiResponse<?> beforeValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) { public ApiResponse<?> beforeValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) {
// 校验参数 // 校验参数
checkParam(imageCaptchaTrack); checkParam(imageCaptchaTrack);
return ApiResponse.SUCCESS; return ApiResponse.ofSuccess();
} }
@Override @Override
public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) { public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) {
if (!CaptchaUtils.isSliderCaptcha(type)) { if (!CaptchaTypeClassifier.isSliderCaptcha(type)) {
// 不是滑动验证码的话暂时跳过,点选验证码行为轨迹还没做 // 不是滑动验证码的话暂时跳过,点选验证码行为轨迹还没做
return ApiResponse.ofSuccess(); return ApiResponse.ofSuccess();
} }
@@ -125,9 +122,9 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
throw new IllegalArgumentException("trackList must not be null"); throw new IllegalArgumentException("trackList must not be null");
} }
for (ImageCaptchaTrack.Track track : imageCaptchaTrack.getTrackList()) { for (ImageCaptchaTrack.Track track : imageCaptchaTrack.getTrackList()) {
Integer x = track.getX(); Float x = track.getX();
Integer y = track.getY(); Float y = track.getY();
Integer t = track.getT(); Float t = track.getT();
String type = track.getType(); String type = track.getType();
if (x == null || y == null || t == null || ObjectUtils.isEmpty(type)) { if (x == null || y == null || t == null || ObjectUtils.isEmpty(type)) {
throw new IllegalArgumentException("track[x,y,t,type] must not be null"); throw new IllegalArgumentException("track[x,y,t,type] must not be null");
@@ -3,7 +3,7 @@ package cloud.tianai.captcha.validator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.response.ApiResponse; import cloud.tianai.captcha.common.response.ApiResponse;
import cloud.tianai.captcha.common.response.ApiResponseStatusConstant; import cloud.tianai.captcha.common.response.ApiResponseStatusConstant;
import cloud.tianai.captcha.common.util.CaptchaUtils; import cloud.tianai.captcha.common.util.CaptchaTypeClassifier;
import cloud.tianai.captcha.common.util.CollectionUtils; import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.ObjectUtils; import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.generator.common.model.dto.ClickImageCheckDefinition; import cloud.tianai.captcha.generator.common.model.dto.ClickImageCheckDefinition;
@@ -38,15 +38,23 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
public static final String TOLERANT_KEY = "tolerant"; public static final String TOLERANT_KEY = "tolerant";
/** 类型 key, 标识是哪张类型的验证码. */ /** 类型 key, 标识是哪张类型的验证码. */
public static final String TYPE_KEY = "type"; public static final String TYPE_KEY = "type";
/** 计算当前验证码用户滑动的百分比率 - 生成时的百分比率, 多个的话取均值. */
public static final String USER_CURRENT_PERCENTAGE_STD = "user_current_percentage_std";
public static final String USER_CURRENT_PERCENTAGE = "user_current_percentage";
/** 容错值. */ /** 容错值. */
@Getter @Getter
@Setter @Setter
public float defaultTolerant = DEFAULT_TOLERANT; public float defaultTolerant = DEFAULT_TOLERANT;
public SimpleImageCaptchaValidator() { public SimpleImageCaptchaValidator() {
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.CONCAT);
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.ROTATE);
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.SLIDER);
CaptchaTypeClassifier.addClickCaptchaType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
} }
public SimpleImageCaptchaValidator(float defaultTolerant) { public SimpleImageCaptchaValidator(float defaultTolerant) {
this();
this.defaultTolerant = defaultTolerant; this.defaultTolerant = defaultTolerant;
} }
@@ -106,10 +114,10 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
// type // type
String type = (String) map.getOrDefault(TYPE_KEY, CaptchaTypeConstant.SLIDER); String type = (String) map.getOrDefault(TYPE_KEY, CaptchaTypeConstant.SLIDER);
Object expand = imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getExpand(); Object expand = imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getExpand();
if (CaptchaUtils.isSliderCaptcha(type)) { if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
// 滑动验证码 // 滑动验证码
addPercentage(imageCaptchaInfo, map); addPercentage(imageCaptchaInfo, map);
} else if (CaptchaUtils.isClickCaptcha(type)) { } else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
// 图片点选验证码 // 图片点选验证码
if (expand == null) { if (expand == null) {
throw new IllegalArgumentException("点选验证码扩展数据转换为 List<ClickImageCheckDefinition> 失败, info=" + imageCaptchaInfo); throw new IllegalArgumentException("点选验证码扩展数据转换为 List<ClickImageCheckDefinition> 失败, info=" + imageCaptchaInfo);
@@ -139,7 +147,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
} }
// 添加点选验证数据 // 添加点选验证数据
map.put(PERCENTAGE_KEY, sb.toString()); map.put(PERCENTAGE_KEY, sb.toString());
} else if (CaptchaUtils.isJigsawCaptcha(type)) { } else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
// 拼图验证码 // 拼图验证码
map.put(PERCENTAGE_KEY, expand); map.put(PERCENTAGE_KEY, expand);
} }
@@ -173,7 +181,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
if (valid) { if (valid) {
// 验证后 // 验证后
response = afterValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type); response = afterValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
}else { } else {
// 缺口位置校验失败 // 缺口位置校验失败
response = ApiResponse.ofMessage(ApiResponseStatusConstant.BASIC_CHECK_FAIL); response = ApiResponse.ofMessage(ApiResponseStatusConstant.BASIC_CHECK_FAIL);
} }
@@ -202,7 +210,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
* @param type type * @param type type
* @return boolean * @return boolean
*/ */
public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) { public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) {
return ApiResponse.ofSuccess(); return ApiResponse.ofSuccess();
} }
@@ -210,13 +218,13 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
Map<String, Object> imageCaptchaValidData, Map<String, Object> imageCaptchaValidData,
Float tolerant, Float tolerant,
String type) { String type) {
if (CaptchaUtils.isSliderCaptcha(type)) { if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
// 滑动类型验证码 // 滑动类型验证码
return doValidSliderCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type); return doValidSliderCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
} else if (CaptchaUtils.isClickCaptcha(type)) { } else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
// 点选类型验证码 // 点选类型验证码
return doValidClickCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type); return doValidClickCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
} else if (CaptchaUtils.isJigsawCaptcha(type)) { } else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
// 拼图类型验证码 // 拼图类型验证码
return doValidJigsawCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type); return doValidJigsawCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
} }
@@ -237,10 +245,10 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
/** /**
* 校验点选验证码 * 校验点选验证码
* *
* @param imageCaptchaTrack sliderCaptchaTrack * @param imageCaptchaTrack sliderCaptchaTrack
* @param imageCaptchaValidData imageCaptchaValidData * @param imageCaptchaValidData imageCaptchaValidData
* @param tolerant tolerant * @param tolerant tolerant
* @param type type * @param type type
* @return boolean * @return boolean
*/ */
public boolean doValidClickCaptcha(ImageCaptchaTrack imageCaptchaTrack, public boolean doValidClickCaptcha(ImageCaptchaTrack imageCaptchaTrack,
@@ -292,10 +300,10 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
/** /**
* 校验滑动验证码 * 校验滑动验证码
* *
* @param imageCaptchaTrack sliderCaptchaTrack * @param imageCaptchaTrack sliderCaptchaTrack
* @param imageCaptchaValidData imageCaptchaValidData * @param imageCaptchaValidData imageCaptchaValidData
* @param tolerant tolerant * @param tolerant tolerant
* @param type type * @param type type
* @return boolean * @return boolean
*/ */
public boolean doValidSliderCaptcha(ImageCaptchaTrack imageCaptchaTrack, public boolean doValidSliderCaptcha(ImageCaptchaTrack imageCaptchaTrack,
@@ -313,7 +321,14 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
// 计算百分比 // 计算百分比
float calcPercentage = calcPercentage(lastTrack.getX(), imageCaptchaTrack.getBgImageWidth()); float calcPercentage = calcPercentage(lastTrack.getX(), imageCaptchaTrack.getBgImageWidth());
// 校验百分比 // 校验百分比
return checkPercentage(calcPercentage, oriPercentage, tolerant); boolean percentage = checkPercentage(calcPercentage, oriPercentage, tolerant);
if (percentage) {
// 校验成功
// 存储一下当前计算出来的值
imageCaptchaValidData.put(USER_CURRENT_PERCENTAGE, String.valueOf(calcPercentage));
imageCaptchaValidData.put(USER_CURRENT_PERCENTAGE_STD, String.valueOf(calcPercentage - oriPercentage));
}
return percentage;
} }
public Float getFloatParam(String key, Map<String, Object> imageCaptchaValidData) { public Float getFloatParam(String key, Map<String, Object> imageCaptchaValidData) {