修复以及优化很多

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>
<groupId>cloud.tianai.captcha</groupId>
<artifactId>tianai-captcha</artifactId>
<version>1.4.2</version>
<version>1.5.0</version>
<name>tianai-captcha</name>
<description>行为验证码</description>
@@ -11,19 +11,8 @@ public interface CaptchaTypeConstant {
String SLIDER = "SLIDER";
/** 旋转. */
String ROTATE = "ROTATE";
/** 旋转角度.*/
String ROTATE_DEGREE = "ROTATE_DEGREE";
/** 拼接.*/
/** 拼接. */
String CONCAT = "CONCAT";
/** 拼图.*/
String JIGSAW = "JIGSAW";
/** 图片点选.*/
String IMAGE_CLICK = "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 {
String DEFAULT_TAG = "default";
/** 图标点选资源存储类型.*/
/** 图标点选资源存储类型. */
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
* @see SortedSet
* @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet
* @see LinkedHashSet
*/
public static <T> T firstElement(Set<T> set) {
if (isEmpty(set)) {
@@ -307,7 +307,7 @@ public abstract class CollectionUtils {
* @since 5.0.3
* @see SortedSet
* @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet
* @see LinkedHashSet
*/
public static <T> T lastElement(Set<T> set) {
if (isEmpty(set)) {
@@ -2,9 +2,7 @@ package cloud.tianai.captcha.common.util;
import lombok.SneakyThrows;
import java.awt.*;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: 天爱有情
@@ -17,11 +17,7 @@
package cloud.tianai.captcha.common.util;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.*;
/**
* 拷贝spring
@@ -60,9 +56,9 @@ public abstract class ObjectUtils {
* that is, neither a RuntimeException nor an Error.
* @param ex the throwable to check
* @return whether the throwable is a checked exception
* @see java.lang.Exception
* @see java.lang.RuntimeException
* @see java.lang.Error
* @see Exception
* @see RuntimeException
* @see Error
*/
public static boolean isCheckedException(Throwable ex) {
return !(ex instanceof RuntimeException || ex instanceof Error);
@@ -125,8 +121,8 @@ public abstract class ObjectUtils {
* @since 4.2
* @see Optional#isPresent()
* @see ObjectUtils#isEmpty(Object[])
* @see CollectionUtils#isEmpty(java.util.Collection)
* @see CollectionUtils#isEmpty(java.util.Map)
* @see CollectionUtils#isEmpty(Collection)
* @see CollectionUtils#isEmpty(Map)
*/
@SuppressWarnings("rawtypes")
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
* @return either the value held within the {@code Optional}, {@code null}
* 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
* @return whether the given objects are equal
* @see Object#equals(Object)
* @see java.util.Arrays#equals
* @see Arrays#equals
*/
public static boolean nullSafeEquals(Object o1, Object o2) {
if (o1 == o2) {
@@ -333,7 +329,7 @@ public abstract class ObjectUtils {
* @param o2 second array to compare
* @return whether the given objects are equal
* @see #nullSafeEquals(Object, Object)
* @see java.util.Arrays#equals
* @see Arrays#equals
*/
private static boolean arrayEquals(Object o1, Object o2) {
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.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.GenerateParam;
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.InputStream;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
@@ -102,21 +101,21 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
assertInit();
CustomData data = new CustomData();
CaptchaTransferData transferData = CaptchaTransferData.create(data, param);
ImageCaptchaInfo imageCaptchaInfo = applyPostProcessorBeforeGenerate(transferData, this);
CaptchaExchange captchaExchange = CaptchaExchange.create(data, param);
ImageCaptchaInfo imageCaptchaInfo = applyPostProcessorBeforeGenerate(captchaExchange, this);
if (imageCaptchaInfo != null) {
return imageCaptchaInfo;
}
doGenerateCaptchaImage(transferData);
applyPostProcessorBeforeWrapImageCaptchaInfo(transferData, this);
imageCaptchaInfo = wrapImageCaptchaInfo(transferData);
applyPostProcessorAfterGenerateCaptchaImage(transferData, imageCaptchaInfo, this);
doGenerateCaptchaImage(captchaExchange);
applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
imageCaptchaInfo = wrapImageCaptchaInfo(captchaExchange);
applyPostProcessorAfterGenerateCaptchaImage(captchaExchange, imageCaptchaInfo, this);
return imageCaptchaInfo;
}
public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaTransferData transferData) {
ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(transferData);
imageCaptchaInfo.setData(transferData.getCustomData());
public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(captchaExchange);
imageCaptchaInfo.setData(captchaExchange.getCustomData());
return imageCaptchaInfo;
}
@@ -225,12 +224,12 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
/**
* 生成验证码方法
*
* @param transferData transferData
* @param captchaExchange captchaExchange
* @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
public ImageCaptchaResourceManager getImageResourceManager() {
@@ -41,7 +41,7 @@ public interface ImageCaptchaGenerator {
ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName);
/**
* 生成滑块验证码
* 生成验证码
*
* @param param 生成参数
* @return SliderCaptchaInfo
@@ -24,5 +24,7 @@ public interface ImageCaptchaGeneratorProvider {
*
* @return String
*/
String getType();
default String getType() {
return "unknown";
}
}
@@ -1,7 +1,6 @@
package cloud.tianai.captcha.generator;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
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;
/**
@@ -11,15 +10,35 @@ import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
*/
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;
}
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 param 参数
* @param backgroundResource 背景资源对象
* @param data 自定义透传数据
* @return ImageTransformData
*/
default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource,CustomData data) {
return transform(param, backgroundImage, null, backgroundResource, null,data);
default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource, CustomData data) {
return transform(param, backgroundImage, null, backgroundResource, null, data);
}
/**
@@ -34,6 +35,7 @@ public interface ImageTransform {
* @param param 参数
* @param backgroundResource 背景资源对象
* @param templateResource 模板资源对象(可能为空)
* @param data 自定义透传数据
* @return String
*/
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 传输用
*/
@Data
public class CaptchaTransferData {
public class CaptchaExchange {
/** 模板对象. */
private ResourceMap templateResource;
/** 资源对象. */
private Resource resourceImage;
/** 生成好的背景图片. */
private BufferedImage backgroundImage;
/** 生成好的模板图片. */
private BufferedImage templateImage;
/** 最终要回调给验证器的自定义对象. */
private CustomData customData;
/** 用户传来的生成参数. */
private GenerateParam param;
/** 传输对象,扩展自定义. */
private Object transferData;
public static CaptchaTransferData create(CustomData customData, GenerateParam param) {
CaptchaTransferData captchaTransferData = new CaptchaTransferData();
captchaTransferData.setCustomData(customData);
captchaTransferData.setParam(param);
return captchaTransferData;
public static CaptchaExchange create(CustomData customData, GenerateParam param) {
CaptchaExchange captchaExchange = new CaptchaExchange();
captchaExchange.setCustomData(customData);
captchaExchange.setParam(param);
return captchaExchange;
}
public static CaptchaTransferData create(GenerateParam param) {
public static CaptchaExchange create(GenerateParam param) {
return create(new CustomData(), param);
}
}
@@ -5,6 +5,8 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.awt.*;
/**
* @Author: 天爱有情
* @date 2022/4/28 16:51
@@ -24,5 +26,7 @@ public class ClickImageCheckDefinition {
private Integer width;
/** 高.*/
private Integer height;
/** 颜色.*/
private Color imageColor;
}
@@ -36,7 +36,6 @@ public class ImageCaptchaInfo {
private Float tolerant;
/** 验证码类型. */
private String type;
/** 透传字段,用于传给前端. */
private CustomData data;
public ImageCaptchaInfo(String backgroundImage,
@@ -9,15 +9,11 @@ import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.awt.image.RenderedImage;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @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图形
*
@@ -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) {
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();
} else {
// 垂直方向
graphics.drawImage(img, 0, pos, img.getWidth(), img.getHeight(),null);
graphics.drawImage(img, 0, pos, img.getWidth(), img.getHeight(), null);
pos += img.getHeight();
}
}
@@ -366,10 +281,6 @@ public class CaptchaImageUtils {
return newImage;
}
public static char getRandomChar() {
return (char) (0x4e00 + (int) (Math.random() * (0x9fa5 - 0x4e00 + 1)));
}
@SneakyThrows
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
@@ -595,30 +483,6 @@ public class CaptchaImageUtils {
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;
}
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
*
@@ -672,3 +521,4 @@ public class CaptchaImageUtils {
}
}
@@ -1,10 +1,9 @@
package cloud.tianai.captcha.generator.impl;
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.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
@@ -13,10 +12,10 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: 天爱有情
@@ -25,6 +24,7 @@ import java.util.concurrent.ThreadLocalRandom;
*/
public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
public static final String CLICK_IMAGE_DISTORT_KEY = "clickImageDistort";
public AbstractClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager);
@@ -35,8 +35,8 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
@SneakyThrows
@Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
// 文字点选验证码不需要模板 只需要背景图
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
@@ -65,19 +65,20 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
// 随机y
int randomY = randomInt(10, bgImage.getHeight() - clickImgHeight);
// 通过随机x和y 进行覆盖图片
CaptchaImageUtils.overlayImage(bgImage, imgWrapper.getImage(), randomX, randomY);
CaptchaImageUtils.overlayImage(bgImage, image, randomX, randomY);
ClickImageCheckDefinition clickImageCheckDefinition = new ClickImageCheckDefinition();
clickImageCheckDefinition.setTip(imgWrapper.getTip());
clickImageCheckDefinition.setX(randomX + clickImgWidth / 2);
clickImageCheckDefinition.setY(randomY + clickImgHeight / 2);
clickImageCheckDefinition.setWidth(clickImgWidth);
clickImageCheckDefinition.setHeight(clickImgHeight);
clickImageCheckDefinition.setImageColor(imgWrapper.getImageColor());
clickImageCheckDefinitionList.add(clickImageCheckDefinition);
}
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList);
transferData.setBackgroundImage(bgImage);
transferData.setTransferData(checkClickImageCheckDefinitionList);
transferData.setResourceImage(resourceImage);
captchaExchange.setBackgroundImage(bgImage);
captchaExchange.setTransferData(checkClickImageCheckDefinitionList);
captchaExchange.setResourceImage(resourceImage);
// // wrap
@@ -123,5 +124,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
private BufferedImage image;
/** 提示. */
private Resource tip;
/** 图片颜色. */
private Color imageColor;
}
}
@@ -1,18 +1,14 @@
package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.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.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.impl.provider.StandardConcatImageCaptchaGeneratorProvider;
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.generator.impl.provider.CommonImageCaptchaGeneratorProvider;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import lombok.Getter;
import lombok.Setter;
@@ -21,6 +17,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static cloud.tianai.captcha.common.constant.CaptchaTypeConstant.*;
/**
* @Author: 天爱有情
* @date 2022/4/24 9:27
@@ -33,7 +31,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Setter
@Getter
private String defaultCaptcha = CaptchaTypeConstant.SLIDER;
private String defaultCaptcha = SLIDER;
protected boolean initDefaultResource = false;
@@ -49,14 +47,15 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Override
protected void doInit(boolean 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) {
@@ -96,12 +95,12 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
@Override
protected void doGenerateCaptchaImage(CaptchaTransferData transferData) {
protected void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
}
@Override
protected ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
protected ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
return null;
}
@@ -11,15 +11,11 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows;
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.generator.common.util.CaptchaImageUtils.*;
import static cloud.tianai.captcha.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.concatImage;
import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.splitImage;
/**
* @Author: 天爱有情
@@ -51,8 +47,8 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
}
@Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
// 拼接验证码不需要模板 只需要背景图
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
BufferedImage bgImage = getResourceImage(resourceImage);
@@ -72,9 +68,9 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
data.x = randomX;
data.y = randomY;
transferData.setTransferData(data);
transferData.setBackgroundImage(bgImage);
transferData.setResourceImage(resourceImage);
captchaExchange.setTransferData(data);
captchaExchange.setBackgroundImage(bgImage);
captchaExchange.setResourceImage(resourceImage);
}
public static class Data {
@@ -84,13 +80,13 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows
@Override
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
BufferedImage bgImage = transferData.getBackgroundImage();
Resource resourceImage = transferData.getResourceImage();
CustomData customData = transferData.getCustomData();
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
BufferedImage bgImage = captchaExchange.getBackgroundImage();
Resource resourceImage = captchaExchange.getResourceImage();
CustomData customData = captchaExchange.getCustomData();
ImageTransformData transform = getImageTransform().transform(param, bgImage, resourceImage, customData);
Data data = (Data) transferData.getTransferData();
Data data = (Data) captchaExchange.getTransferData();
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(transform.getBackgroundImageUrl(),
null,
resourceImage.getTag(),
@@ -4,7 +4,6 @@ import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CommonConstant;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
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.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
@@ -15,14 +14,9 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.Optional;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.*;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.TEMPLATE_MASK_IMAGE_NAME;
import static cloud.tianai.captcha.common.constant.CommonConstant.*;
/**
* @Author: 天爱有情
@@ -31,6 +25,13 @@ import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConsta
*/
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) {
super(imageCaptchaResourceManager);
}
@@ -50,18 +51,24 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
public void initDefaultResource() {
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);
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.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_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, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
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
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
CustomData data = new CustomData();
ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
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);
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);
RotateData rotateData = new RotateData();
rotateData.degree = degree;
rotateData.randomX = randomX;
transferData.setTransferData(rotateData);
transferData.setBackgroundImage(background);
transferData.setTemplateImage(matrixTemplate);
transferData.setTemplateResource(templateResource);
transferData.setResourceImage(resourceImage);
captchaExchange.setTransferData(rotateData);
captchaExchange.setBackgroundImage(background);
captchaExchange.setTemplateImage(matrixTemplate);
captchaExchange.setTemplateResource(templateResource);
captchaExchange.setResourceImage(resourceImage);
// return wrapRotateCaptchaInfo(degree, randomX, background, matrixTemplate, param, templateResource, resourceImage, data);
}
@@ -125,14 +132,14 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows
@Override
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
BufferedImage backgroundImage = transferData.getBackgroundImage();
BufferedImage sliderImage = transferData.getTemplateImage();
Resource resourceImage = transferData.getResourceImage();
ResourceMap templateResource = transferData.getTemplateResource();
CustomData data = transferData.getCustomData();
RotateData rotateData = (RotateData) transferData.getTransferData();
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
BufferedImage backgroundImage = captchaExchange.getBackgroundImage();
BufferedImage sliderImage = captchaExchange.getTemplateImage();
Resource resourceImage = captchaExchange.getResourceImage();
ResourceMap templateResource = captchaExchange.getTemplateResource();
CustomData data = captchaExchange.getCustomData();
RotateData rotateData = (RotateData) captchaExchange.getTransferData();
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, data);
RotateImageCaptchaInfo imageCaptchaInfo = RotateImageCaptchaInfo.of(rotateData.degree,
rotateData.randomX,
@@ -3,7 +3,6 @@ package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
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.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
@@ -16,14 +15,10 @@ import lombok.extern.slf4j.Slf4j;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.*;
import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME;
import static cloud.tianai.captcha.common.constant.CommonConstant.*;
/**
* @Author: 天爱有情
@@ -33,14 +28,16 @@ import static cloud.tianai.captcha.generator.common.constant.SliderCaptchaConsta
@Slf4j
public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/**
* 默认的resource资源文件路径.
*/
public static final String DEFAULT_SLIDER_IMAGE_RESOURCE_PATH = "META-INF/cut-image/resource";
/**
* 默认的template资源文件路径.
*/
public static final String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template";
/** 模板滑块固定名称. */
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 static String OBFUSCATE_TEMPLATE_FIXED_IMAGE_NAME = "obfuscate_" + TEMPLATE_FIXED_IMAGE_NAME;
public StandardSliderImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager);
@@ -60,8 +57,8 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows
@Override
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
Boolean obfuscate = param.getObfuscate();
ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
@@ -93,15 +90,15 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
XandY xandY = new XandY();
xandY.x = randomX;
xandY.y = randomY;
transferData.setBackgroundImage(background);
transferData.setTemplateImage(matrixTemplate);
transferData.setTemplateResource(templateResource);
transferData.setResourceImage(resourceImage);
transferData.setTransferData(xandY);
captchaExchange.setBackgroundImage(background);
captchaExchange.setTemplateImage(matrixTemplate);
captchaExchange.setTemplateResource(templateResource);
captchaExchange.setResourceImage(resourceImage);
captchaExchange.setTransferData(xandY);
// 后处理
// applyPostProcessorBeforeWrapImageCaptchaInfo(transferData, this);
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, transferData);
// applyPostProcessorAfterGenerateCaptchaImage(transferData, imageCaptchaInfo, this);
// applyPostProcessorBeforeWrapImageCaptchaInfo(captchaExchange, this);
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, captchaExchange);
// applyPostProcessorAfterGenerateCaptchaImage(captchaExchange, imageCaptchaInfo, this);
// return imageCaptchaInfo;
}
@@ -134,14 +131,14 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
@SneakyThrows
@Override
public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
GenerateParam param = transferData.getParam();
BufferedImage backgroundImage = transferData.getBackgroundImage();
BufferedImage sliderImage = transferData.getTemplateImage();
Resource resourceImage = transferData.getResourceImage();
ResourceMap templateResource = transferData.getTemplateResource();
CustomData customData = transferData.getCustomData();
XandY data = (XandY) transferData.getTransferData();
public SliderImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
GenerateParam param = captchaExchange.getParam();
BufferedImage backgroundImage = captchaExchange.getBackgroundImage();
BufferedImage sliderImage = captchaExchange.getTemplateImage();
Resource resourceImage = captchaExchange.getResourceImage();
ResourceMap templateResource = captchaExchange.getTemplateResource();
CustomData customData = captchaExchange.getCustomData();
XandY data = (XandY) captchaExchange.getTransferData();
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData);
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.FontUtils;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.FontWrapper;
import cloud.tianai.captcha.generator.common.model.dto.*;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
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.image.BufferedImage;
@@ -22,6 +25,7 @@ import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
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;
/**
@@ -114,7 +118,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
public void initDefaultResource() {
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) {
@@ -130,7 +134,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
float top = 6 / 2f + font.getSize() - currentFontTopCoef;
BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips,
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
@@ -150,7 +154,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
clickImgWidth,
clickImgHeight,
randomDeg);
return new ImgWrapper(fontImage, tip);
return new ImgWrapper(fontImage, tip, randomColor);
}
@Override
@@ -167,12 +171,12 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
}
@Override
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = (List<ClickImageCheckDefinition>) transferData.getTransferData();
BufferedImage bgImage = transferData.getBackgroundImage();
GenerateParam param = transferData.getParam();
Resource resourceImage = transferData.getResourceImage();
CustomData data = transferData.getCustomData();
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaExchange captchaExchange) {
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = (List<ClickImageCheckDefinition>) captchaExchange.getTransferData();
BufferedImage bgImage = captchaExchange.getBackgroundImage();
GenerateParam param = captchaExchange.getParam();
Resource resourceImage = captchaExchange.getResourceImage();
CustomData data = captchaExchange.getCustomData();
// 提示图片
BufferedImage tipImage = genTipImage(checkClickImageCheckDefinitionList).getImage();
ImageTransformData transform = getImageTransform().transform(param, bgImage, tipImage, resourceImage, checkClickImageCheckDefinitionList, data);
@@ -191,12 +195,5 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
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.generator.ImageCaptchaGenerator;
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 lombok.Getter;
@@ -20,10 +20,6 @@ public class StaticCaptchaPostProcessorManager {
@Getter
private static LinkedList<ImageCaptchaPostProcessor> processors = new LinkedList<>();
static {
}
public static void add(ImageCaptchaPostProcessor 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) {
try {
ImageCaptchaInfo imageCaptchaInfo = processor.beforeGenerateCaptchaImage(transferData, context);
ImageCaptchaInfo imageCaptchaInfo = processor.beforeGenerateCaptchaImage(captchaExchange, context);
if (imageCaptchaInfo != null) {
return imageCaptchaInfo;
}
@@ -63,10 +59,10 @@ public class StaticCaptchaPostProcessorManager {
return null;
}
public static void applyPostProcessorBeforeWrapImageCaptchaInfo(CaptchaTransferData transferData, ImageCaptchaGenerator context) {
public static void applyPostProcessorBeforeWrapImageCaptchaInfo(CaptchaExchange captchaExchange, ImageCaptchaGenerator context) {
for (ImageCaptchaPostProcessor processor : processors) {
try {
processor.beforeWrapImageCaptchaInfo(transferData, context);
processor.beforeWrapImageCaptchaInfo(captchaExchange, context);
} catch (Exception 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) {
try {
processor.afterGenerateCaptchaImage(transferData, imageCaptchaInfo, context);
processor.afterGenerateCaptchaImage(captchaExchange, imageCaptchaInfo, context);
} catch (Exception e) {
throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.afterGenerateCaptchaImage error, [" + processor.getClass() + "]", e);
}
@@ -1,20 +1,28 @@
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.StandardWordClickImageCaptchaGenerator;
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
public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) {
return new StandardWordClickImageCaptchaGenerator(resourceManager, imageTransform);
return provider.get(resourceManager, imageTransform);
}
@Override
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.util.CaptchaImageUtils;
import cloud.tianai.captcha.generator.common.util.ImgWriter;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.SneakyThrows;
import javax.imageio.ImageIO;
@@ -60,12 +59,7 @@ public class Base64ImageTransform implements ImageTransform {
}
@Override
public ImageTransformData transform(GenerateParam param,
BufferedImage backgroundImage,
BufferedImage templateImage,
Object backgroundResource,
Object templateResource,
CustomData data) {
public ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, BufferedImage templateImage, Object backgroundResource, Object templateResource, CustomData data) {
ImageTransformData imageTransformData = new ImageTransformData();
if (backgroundImage != null) {
imageTransformData.setBackgroundImageUrl(transform(backgroundImage, param.getBackgroundFormatName()));
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
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.ResourceMap;
import java.util.Map;
/**
* @Author: 天爱有情
* @date 2022/5/7 9:04
@@ -1,7 +1,6 @@
package cloud.tianai.captcha.resource.common.model.dto;
import cloud.tianai.captcha.resource.ResourceProvider;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -19,7 +18,10 @@ public class Resource {
public String data;
/** 标签.*/
private String tag;
/** 提示.*/
private String tip;
/** 扩展.*/
private Object extra;
public Resource(String type, String data) {
this.type = type;
this.data = data;
@@ -30,4 +32,11 @@ public class Resource {
this.data = data;
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;
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.ResourceProvider;
import cloud.tianai.captcha.resource.ResourceStore;
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.FileResourceProvider;
import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider;
@@ -15,7 +13,6 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @Author: 天爱有情
@@ -41,7 +38,7 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
private void init() {
if (this.resourceStore == null) {
this.resourceStore = new DefaultResourceStore();
this.resourceStore = new LocalMemoryResourceStore();
}
// 注入一些默认的提供者
registerResourceProvider(new URLResourceProvider());
@@ -1,5 +1,6 @@
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.ObjectUtils;
import cloud.tianai.captcha.resource.ResourceStore;
@@ -14,17 +15,8 @@ import java.util.concurrent.ThreadLocalRandom;
* @date 2021/8/7 15:43
* @Description 默认的资源存储
*/
public class DefaultResourceStore implements ResourceStore {
public class LocalMemoryResourceStore implements ResourceStore {
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. */
private Map<String, List<ResourceMap>> templateResourceTagMap = new HashMap<>(2);
@@ -32,32 +24,25 @@ public class DefaultResourceStore implements ResourceStore {
@Override
public void addResource(String type, Resource resource) {
resourceMap.computeIfAbsent(type, k -> new ArrayList<>(20)).add(resource);
// 添加tag标签字典
if (!ObjectUtils.isEmpty(resource.getTag())) {
resourceTagMap.computeIfAbsent(mergeTypeAndTag(type, resource.getTag()), k -> new ArrayList<>(20)).add(resource);
if (ObjectUtils.isEmpty(resource.getTag())) {
resource.setTag(CommonConstant.DEFAULT_TAG);
}
resourceTagMap.computeIfAbsent(mergeTypeAndTag(type, resource.getTag()), k -> new ArrayList<>(20)).add(resource);
}
@Override
public void addTemplate(String type, ResourceMap template) {
templateResourceMap.computeIfAbsent(type, k -> new ArrayList<>(2)).add(template);
// 添加tag标签字典
if (!ObjectUtils.isEmpty(template.getTag())) {
templateResourceTagMap.computeIfAbsent(mergeTypeAndTag(type, template.getTag()), k -> new ArrayList<>(2)).add(template);
if (ObjectUtils.isEmpty(template.getTag())) {
template.setTag(CommonConstant.DEFAULT_TAG);
}
templateResourceTagMap.computeIfAbsent(mergeTypeAndTag(type, template.getTag()), k -> new ArrayList<>(2)).add(template);
}
@Override
public Resource randomGetResourceByTypeAndTag(String type, String tag) {
List<Resource> resources;
if (ObjectUtils.isEmpty(tag)) {
resources = resourceMap.get(type);
} else {
resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
}
List<Resource> resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
if (CollectionUtils.isEmpty(resources)) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
}
if (resources.size() == 1) {
return resources.get(0);
@@ -68,16 +53,10 @@ public class DefaultResourceStore implements ResourceStore {
@Override
public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) {
List<ResourceMap> templateList;
if (ObjectUtils.isEmpty(tag)) {
templateList = templateResourceMap.get(type);
} else {
templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
}
List<ResourceMap> templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
if (CollectionUtils.isEmpty(templateList)) {
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
}
if (templateList.size() == 1) {
return templateList.get(0);
}
@@ -86,53 +65,56 @@ public class DefaultResourceStore implements ResourceStore {
}
public String mergeTypeAndTag(String type, String tag) {
if (tag == null) {
tag = CommonConstant.DEFAULT_TAG;
}
return type + TYPE_TAG_SPLIT_FLAG + tag;
}
public void clearResources(String type) {
resourceMap.remove(type);
public void clearResources(String type, String tag) {
resourceTagMap.remove(mergeTypeAndTag(type, tag));
}
public void clearAllResources() {
resourceMap.clear();
resourceTagMap.clear();
}
public Map<String, List<Resource>> listAllResources() {
return resourceMap;
return resourceTagMap;
}
public List<Resource> listResourcesByType(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList());
public List<Resource> listResourcesByType(String type, String tag) {
return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
}
public int getAllResourceCount() {
int count = 0;
for (List<Resource> value : resourceMap.values()) {
for (List<Resource> value : resourceTagMap.values()) {
count += value.size();
}
return count;
}
public int getResourceCount(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList()).size();
public int getResourceCount(String type, String tag) {
return resourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList()).size();
}
public void clearAllTemplates() {
templateResourceMap.clear();
templateResourceTagMap.clear();
}
public void clearTemplates(String type) {
templateResourceMap.remove(type);
public void clearTemplates(String type, String tag) {
templateResourceTagMap.remove(mergeTypeAndTag(type, tag));
}
public List<ResourceMap> listTemplatesByType(String type) {
return templateResourceMap.getOrDefault(type, Collections.emptyList());
public List<ResourceMap> listTemplatesByType(String type, String tag) {
return templateResourceTagMap.getOrDefault(mergeTypeAndTag(type, tag), Collections.emptyList());
}
public Map<String, List<ResourceMap>> listAllTemplates() {
return templateResourceMap;
public Map<String, List<ResourceMap>> listAllTemplates() {
return templateResourceTagMap;
}
@@ -1,7 +1,5 @@
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.common.model.dto.Resource;
@@ -1,7 +1,5 @@
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.common.model.dto.Resource;
import lombok.SneakyThrows;
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.resource.impl.provider;
import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.AbstractResourceProvider;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.SneakyThrows;
@@ -20,10 +20,10 @@ public class ImageCaptchaTrack {
private Integer bgImageWidth;
/** 背景图片高度. */
private Integer bgImageHeight;
/** 滑块图片宽度. */
private Integer sliderImageWidth;
/** 滑块图片高度. */
private Integer sliderImageHeight;
/** 模板图片宽度. */
private Integer templateImageWidth;
/** 模板图片高度. */
private Integer templateImageHeight;
/** 滑动开始时间. */
private Date startSlidingTime;
/** 滑动结束时间. */
@@ -32,28 +32,16 @@ public class ImageCaptchaTrack {
private List<Track> trackList;
/** 扩展数据,用户传输加密数据等.*/
private Object data;
/**
* 兼容一下旧版本
*
* @return Date
*/
public Date getEntSlidingTime() {
return endSlidingTime;
}
public void setEntSlidingTime(Date entSlidingTime) {
this.endSlidingTime = entSlidingTime;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Track {
/** x. */
private Integer x;
private Float x;
/** y. */
private Integer y;
private Float y;
/** 时间. */
private Integer t;
private Float t;
/** 类型. */
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.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.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 java.util.List;
@@ -21,6 +17,7 @@ import java.util.Map;
*/
public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
public static final CodeDefinition DEFINITION = new CodeDefinition(50001, "basic check fail");
public BasicCaptchaTrackValidator() {
}
@@ -29,15 +26,15 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
}
@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);
return ApiResponse.SUCCESS;
return ApiResponse.ofSuccess();
}
@Override
public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
if (!CaptchaUtils.isSliderCaptcha(type)) {
public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) {
if (!CaptchaTypeClassifier.isSliderCaptcha(type)) {
// 不是滑动验证码的话暂时跳过,点选验证码行为轨迹还没做
return ApiResponse.ofSuccess();
}
@@ -125,9 +122,9 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
throw new IllegalArgumentException("trackList must not be null");
}
for (ImageCaptchaTrack.Track track : imageCaptchaTrack.getTrackList()) {
Integer x = track.getX();
Integer y = track.getY();
Integer t = track.getT();
Float x = track.getX();
Float y = track.getY();
Float t = track.getT();
String type = track.getType();
if (x == null || y == null || t == null || ObjectUtils.isEmpty(type)) {
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.response.ApiResponse;
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.ObjectUtils;
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";
/** 类型 key, 标识是哪张类型的验证码. */
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
@Setter
public float defaultTolerant = DEFAULT_TOLERANT;
public SimpleImageCaptchaValidator() {
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.CONCAT);
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.ROTATE);
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.SLIDER);
CaptchaTypeClassifier.addClickCaptchaType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
}
public SimpleImageCaptchaValidator(float defaultTolerant) {
this();
this.defaultTolerant = defaultTolerant;
}
@@ -106,10 +114,10 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
// type
String type = (String) map.getOrDefault(TYPE_KEY, CaptchaTypeConstant.SLIDER);
Object expand = imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getExpand();
if (CaptchaUtils.isSliderCaptcha(type)) {
if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
// 滑动验证码
addPercentage(imageCaptchaInfo, map);
} else if (CaptchaUtils.isClickCaptcha(type)) {
} else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
// 图片点选验证码
if (expand == null) {
throw new IllegalArgumentException("点选验证码扩展数据转换为 List<ClickImageCheckDefinition> 失败, info=" + imageCaptchaInfo);
@@ -139,7 +147,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
}
// 添加点选验证数据
map.put(PERCENTAGE_KEY, sb.toString());
} else if (CaptchaUtils.isJigsawCaptcha(type)) {
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
// 拼图验证码
map.put(PERCENTAGE_KEY, expand);
}
@@ -173,7 +181,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
if (valid) {
// 验证后
response = afterValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
}else {
} else {
// 缺口位置校验失败
response = ApiResponse.ofMessage(ApiResponseStatusConstant.BASIC_CHECK_FAIL);
}
@@ -202,7 +210,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
* @param type type
* @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();
}
@@ -210,13 +218,13 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
Map<String, Object> imageCaptchaValidData,
Float tolerant,
String type) {
if (CaptchaUtils.isSliderCaptcha(type)) {
if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
// 滑动类型验证码
return doValidSliderCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
} else if (CaptchaUtils.isClickCaptcha(type)) {
} else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
// 点选类型验证码
return doValidClickCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
} else if (CaptchaUtils.isJigsawCaptcha(type)) {
} else if (CaptchaTypeClassifier.isJigsawCaptcha(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 tolerant tolerant
* @param type type
* @param tolerant tolerant
* @param type type
* @return boolean
*/
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 tolerant tolerant
* @param type type
* @param tolerant tolerant
* @param type type
* @return boolean
*/
public boolean doValidSliderCaptcha(ImageCaptchaTrack imageCaptchaTrack,
@@ -313,7 +321,14 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
// 计算百分比
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) {