mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
重构生成器逻辑,优化图片验证码生成效率
This commit is contained in:
@@ -2,6 +2,8 @@ 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.CustomData;
|
||||
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;
|
||||
@@ -22,6 +24,8 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static cloud.tianai.captcha.generator.impl.StaticCaptchaPostProcessorManager.*;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/4/22 16:30
|
||||
@@ -97,9 +101,24 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
@Override
|
||||
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
|
||||
assertInit();
|
||||
return doGenerateCaptchaImage(param);
|
||||
CustomData data = new CustomData();
|
||||
CaptchaTransferData transferData = CaptchaTransferData.create(data, param);
|
||||
ImageCaptchaInfo imageCaptchaInfo = applyPostProcessorBeforeGenerate(transferData, this);
|
||||
if (imageCaptchaInfo != null) {
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
doGenerateCaptchaImage(transferData);
|
||||
applyPostProcessorBeforeWrapImageCaptchaInfo(transferData, this);
|
||||
imageCaptchaInfo = wrapImageCaptchaInfo(transferData);
|
||||
applyPostProcessorAfterGenerateCaptchaImage(transferData, imageCaptchaInfo, this);
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
|
||||
public ImageCaptchaInfo wrapImageCaptchaInfo(CaptchaTransferData transferData) {
|
||||
ImageCaptchaInfo imageCaptchaInfo = doWrapImageCaptchaInfo(transferData);
|
||||
imageCaptchaInfo.setData(transferData.getCustomData());
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
|
||||
protected ResourceMap requiredRandomGetTemplate(String type, String tag) {
|
||||
ResourceMap templateMap = imageCaptchaResourceManager.randomGetTemplate(type, tag);
|
||||
@@ -144,6 +163,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
protected int randomInt(int origin, int bound) {
|
||||
return ThreadLocalRandom.current().nextInt(origin, bound);
|
||||
}
|
||||
|
||||
protected boolean randomBoolean() {
|
||||
return ThreadLocalRandom.current().nextBoolean();
|
||||
}
|
||||
@@ -205,10 +225,12 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
/**
|
||||
* 生成验证码方法
|
||||
*
|
||||
* @param param param
|
||||
* @param transferData transferData
|
||||
* @return ImageCaptchaInfo
|
||||
*/
|
||||
protected abstract ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param);
|
||||
protected abstract void doGenerateCaptchaImage(CaptchaTransferData transferData);
|
||||
|
||||
protected abstract ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData);
|
||||
|
||||
@Override
|
||||
public ImageCaptchaResourceManager getImageResourceManager() {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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.ImageCaptchaInfo;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/4/24 15:11
|
||||
* @Description 图片验证码后处理器
|
||||
*/
|
||||
public interface ImageCaptchaPostProcessor {
|
||||
|
||||
default ImageCaptchaInfo beforeGenerateCaptchaImage(CaptchaTransferData transferData, ImageCaptchaGenerator context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default void beforeWrapImageCaptchaInfo(CaptchaTransferData transferData, ImageCaptchaGenerator context) {
|
||||
|
||||
}
|
||||
|
||||
default void afterGenerateCaptchaImage(CaptchaTransferData transferData, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator context) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package cloud.tianai.captcha.generator;
|
||||
|
||||
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.ImageTransformData;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
@@ -21,8 +22,8 @@ public interface ImageTransform {
|
||||
* @param backgroundResource 背景资源对象
|
||||
* @return ImageTransformData
|
||||
*/
|
||||
default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource) {
|
||||
return transform(param, backgroundImage, null, backgroundResource, null);
|
||||
default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource,CustomData data) {
|
||||
return transform(param, backgroundImage, null, backgroundResource, null,data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,5 +40,6 @@ public interface ImageTransform {
|
||||
BufferedImage backgroundImage,
|
||||
BufferedImage templateImage,
|
||||
Object backgroundResource,
|
||||
Object templateResource);
|
||||
Object templateResource,
|
||||
CustomData data);
|
||||
}
|
||||
|
||||
+4
-2
@@ -11,6 +11,8 @@ public interface SliderCaptchaConstant {
|
||||
String TEMPLATE_ACTIVE_IMAGE_NAME = "active.png";
|
||||
/** 模板凹槽固定名称. */
|
||||
String TEMPLATE_FIXED_IMAGE_NAME = "fixed.png";
|
||||
/** 模板背景固定名称. */
|
||||
String TEMPLATE_MATRIX_IMAGE_NAME = "matrix.png";
|
||||
/** 模板蒙版. */
|
||||
String TEMPLATE_MASK_IMAGE_NAME = "mask.png";
|
||||
/** 混淆的凹槽.*/
|
||||
String OBFUSCATE_TEMPLATE_FIXED_IMAGE_NAME = "obfuscate_" + TEMPLATE_FIXED_IMAGE_NAME;
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package cloud.tianai.captcha.generator.common.model.dto;
|
||||
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
import lombok.Data;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/4/24 15:02
|
||||
* @Description 传输用
|
||||
*/
|
||||
@Data
|
||||
public class CaptchaTransferData {
|
||||
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 CaptchaTransferData create(GenerateParam param) {
|
||||
return create(new CustomData(), param);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cloud.tianai.captcha.generator.common.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/4/24 10:27
|
||||
* @Description 自定义扩展数据
|
||||
*/
|
||||
@Data
|
||||
public class CustomData {
|
||||
|
||||
/** 透传字段,用于传给前端. */
|
||||
private Map<String, Object> viewData;
|
||||
/** 内部使用的字段数据. */
|
||||
private Map<String, Object> data;
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
public Object expand;
|
||||
|
||||
public void putViewData(String key, Object data) {
|
||||
if (this.viewData == null) {
|
||||
this.viewData = new HashMap<>();
|
||||
}
|
||||
this.viewData.put(key, data);
|
||||
}
|
||||
|
||||
public void putData(String key, Object data) {
|
||||
if (this.data == null) {
|
||||
this.data = new HashMap<>();
|
||||
}
|
||||
this.data.put(key, data);
|
||||
}
|
||||
}
|
||||
@@ -37,11 +37,7 @@ public class ImageCaptchaInfo {
|
||||
/** 验证码类型. */
|
||||
private String type;
|
||||
/** 透传字段,用于传给前端. */
|
||||
private Object data;
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
public Object expand;
|
||||
private CustomData data;
|
||||
|
||||
public ImageCaptchaInfo(String backgroundImage,
|
||||
String templateImage,
|
||||
|
||||
@@ -44,6 +44,11 @@ public class CaptchaImageUtils {
|
||||
return ImageIO.read(resource);
|
||||
}
|
||||
|
||||
public static BufferedImage createTransparentImage(int width, int height) {
|
||||
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 图片覆盖(覆盖图压缩到width*height大小,覆盖到底图上)
|
||||
@@ -63,6 +68,7 @@ public class CaptchaImageUtils {
|
||||
g2d.dispose();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将Image图像中的透明/不透明部分转换为Shape图形
|
||||
*
|
||||
@@ -135,20 +141,44 @@ public class CaptchaImageUtils {
|
||||
/**
|
||||
* 通过模板图片抠图(不透明部分)
|
||||
*
|
||||
* @param origin 源图片
|
||||
* @param template 模板图片
|
||||
* @param x 坐标轴x
|
||||
* @param y 坐标轴y
|
||||
* @param oriImage 源图片
|
||||
* @param templateImage 模板图片
|
||||
* @param xPos 坐标轴x
|
||||
* @param yPos 坐标轴y
|
||||
* @return BufferedImage
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static BufferedImage cutImage(BufferedImage origin, BufferedImage template, int x, int y) {
|
||||
public static BufferedImage cutImage(BufferedImage oriImage, BufferedImage templateImage, int xPos, int yPos) {
|
||||
// 模板图像矩阵
|
||||
int bw = templateImage.getWidth(null);
|
||||
int bh = templateImage.getHeight(null);
|
||||
BufferedImage targetImage = new BufferedImage(bw, bh, BufferedImage.TYPE_INT_ARGB);
|
||||
// 透明色
|
||||
for (int y = 0; y < bh; y++) {
|
||||
for (int x = 0; x < bw; x++) {
|
||||
int rgb = templateImage.getRGB(x, y);
|
||||
int alpha = (rgb >> 24) & 0xff;
|
||||
// 透明度大于100才处理,过滤一下边缘过于透明的像素点
|
||||
if (alpha > 100) {
|
||||
int bgRgb = oriImage.getRGB(xPos + x, yPos + y);
|
||||
targetImage.setRGB(x, y, bgRgb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return targetImage;
|
||||
}
|
||||
|
||||
|
||||
@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();
|
||||
@@ -305,12 +335,6 @@ public class CaptchaImageUtils {
|
||||
return newImage;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
char randomChar = getRandomChar();
|
||||
System.out.println(randomChar);
|
||||
}
|
||||
|
||||
public static char getRandomChar() {
|
||||
return (char) (0x4e00 + (int) (Math.random() * (0x9fa5 - 0x4e00 + 1)));
|
||||
}
|
||||
@@ -426,7 +450,7 @@ public class CaptchaImageUtils {
|
||||
char[] chars = data.toCharArray();
|
||||
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
g.setColor(getRandomColor(random));
|
||||
g.setColor(Color.gray);
|
||||
g.drawString(String.valueOf(chars[i]), startX + i * font.getSize(), startY);
|
||||
}
|
||||
// 干扰点
|
||||
@@ -441,6 +465,24 @@ public class CaptchaImageUtils {
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 随机获取颜色
|
||||
*
|
||||
@@ -522,6 +564,30 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建画板
|
||||
*
|
||||
@@ -539,6 +605,21 @@ 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
|
||||
*
|
||||
|
||||
+12
-15
@@ -1,6 +1,7 @@
|
||||
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.ClickImageCheckDefinition;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
@@ -34,7 +35,8 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
|
||||
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
|
||||
GenerateParam param = transferData.getParam();
|
||||
// 文字点选验证码不需要模板 只需要背景图
|
||||
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
|
||||
|
||||
@@ -73,8 +75,15 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
|
||||
clickImageCheckDefinitionList.add(clickImageCheckDefinition);
|
||||
}
|
||||
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList);
|
||||
// wrap
|
||||
return wrapClickImageCaptchaInfo(param, bgImage, checkClickImageCheckDefinitionList, resourceImage);
|
||||
transferData.setBackgroundImage(bgImage);
|
||||
transferData.setTransferData(checkClickImageCheckDefinitionList);
|
||||
transferData.setResourceImage(resourceImage);
|
||||
|
||||
|
||||
// // wrap
|
||||
// ImageCaptchaInfo imageCaptchaInfo = wrapClickImageCaptchaInfo(param, bgImage, checkClickImageCheckDefinitionList, resourceImage, data);
|
||||
// imageCaptchaInfo.setData(data);
|
||||
// return imageCaptchaInfo;
|
||||
|
||||
}
|
||||
|
||||
@@ -101,18 +110,6 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
|
||||
*/
|
||||
public abstract ImgWrapper getClickImg(Resource tip);
|
||||
|
||||
/**
|
||||
* 包装 ImageCaptchaInfo
|
||||
*
|
||||
* @param param param
|
||||
* @param bgImage bgImage
|
||||
* @param checkClickImageCheckDefinitionList checkClickImageCheckDefinitionList
|
||||
* @param resourceImage 随机读取到的图片资源
|
||||
* @return ImageCaptchaInfo
|
||||
*/
|
||||
public abstract ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
|
||||
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList, Resource resourceImage);
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/4/28 14:26
|
||||
|
||||
@@ -6,10 +6,11 @@ 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.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.StandardRandomWordClickImageCaptchaGeneratorProvider;
|
||||
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;
|
||||
@@ -55,7 +56,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
// 拼接验证码
|
||||
addImageCaptchaGeneratorProvider(new StandardConcatImageCaptchaGeneratorProvider());
|
||||
// 点选文字验证码
|
||||
addImageCaptchaGeneratorProvider(new StandardRandomWordClickImageCaptchaGeneratorProvider());
|
||||
addImageCaptchaGeneratorProvider(new StandardWordClickImageCaptchaGeneratorProvider());
|
||||
}
|
||||
|
||||
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
|
||||
@@ -83,7 +84,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
|
||||
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
|
||||
String type = param.getType();
|
||||
if (ObjectUtils.isEmpty(type)) {
|
||||
param.setType(defaultCaptcha);
|
||||
@@ -93,6 +94,17 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
return imageCaptchaGenerator.generateCaptchaImage(param);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void doGenerateCaptchaImage(CaptchaTransferData transferData) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ImageCaptchaGenerator requireGetCaptchaGenerator(String type) {
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = imageCaptchaGeneratorMap.computeIfAbsent(type, t -> {
|
||||
ImageCaptchaGeneratorProvider provider = imageCaptchaGeneratorProviderMap.get(t);
|
||||
@@ -101,7 +113,6 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
}
|
||||
return provider.get(getImageResourceManager(), getImageTransform()).init(initDefaultResource);
|
||||
});
|
||||
|
||||
return imageCaptchaGenerator;
|
||||
}
|
||||
|
||||
|
||||
+29
-28
@@ -3,9 +3,7 @@ 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.model.dto.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.*;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
@@ -19,6 +17,7 @@ import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
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;
|
||||
|
||||
@@ -48,23 +47,20 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
public void initDefaultResource() {
|
||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||
// 添加一些系统的资源文件
|
||||
resourceStore.addResource(CaptchaTypeConstant.CONCAT, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
|
||||
resourceStore.addResource(CaptchaTypeConstant.CONCAT, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
|
||||
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
|
||||
GenerateParam param = transferData.getParam();
|
||||
// 拼接验证码不需要模板 只需要背景图
|
||||
Collection<InputStream> inputStreams = new LinkedList<>();
|
||||
try {
|
||||
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
|
||||
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
|
||||
inputStreams.add(resourceInputStream);
|
||||
BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream);
|
||||
BufferedImage bgImage = getResourceImage(resourceImage);
|
||||
int spacingY = bgImage.getHeight() / 4;
|
||||
int randomY = ThreadLocalRandom.current().nextInt(spacingY, bgImage.getHeight() - spacingY);
|
||||
int randomY = randomInt(spacingY, bgImage.getHeight() - spacingY);
|
||||
BufferedImage[] bgImageSplit = splitImage(randomY, true, bgImage);
|
||||
int spacingX = bgImage.getWidth() / 8;
|
||||
int randomX = ThreadLocalRandom.current().nextInt(spacingX, bgImage.getWidth() - bgImage.getWidth() / 5);
|
||||
int randomX = randomInt(spacingX, bgImage.getWidth() - bgImage.getWidth() / 5);
|
||||
BufferedImage[] bgImageTopSplit = splitImage(randomX, false, bgImageSplit[0]);
|
||||
|
||||
BufferedImage sliderImage = concatImage(true,
|
||||
@@ -72,24 +68,29 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
+ bgImageTopSplit[1].getWidth(), bgImageTopSplit[0].getHeight(), bgImageTopSplit[1], bgImageTopSplit[0]);
|
||||
bgImage = concatImage(false, bgImageSplit[1].getWidth(), sliderImage.getHeight() + bgImageSplit[1].getHeight(),
|
||||
sliderImage, bgImageSplit[1]);
|
||||
return wrapConcatCaptchaInfo(randomX, randomY, bgImage, param, resourceImage);
|
||||
} finally {
|
||||
// 使用完后关闭流
|
||||
for (InputStream inputStream : inputStreams) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
Data data = new Data();
|
||||
data.x = randomX;
|
||||
data.y = randomY;
|
||||
|
||||
transferData.setTransferData(data);
|
||||
transferData.setBackgroundImage(bgImage);
|
||||
transferData.setResourceImage(resourceImage);
|
||||
}
|
||||
|
||||
public static class Data {
|
||||
int x;
|
||||
int y;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, int randomY, BufferedImage bgImage, GenerateParam param, Resource resourceImage) {
|
||||
|
||||
ImageTransformData transform = getImageTransform().transform(param, bgImage, resourceImage);
|
||||
|
||||
@Override
|
||||
public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) {
|
||||
GenerateParam param = transferData.getParam();
|
||||
BufferedImage bgImage = transferData.getBackgroundImage();
|
||||
Resource resourceImage = transferData.getResourceImage();
|
||||
CustomData customData = transferData.getCustomData();
|
||||
ImageTransformData transform = getImageTransform().transform(param, bgImage, resourceImage, customData);
|
||||
Data data = (Data) transferData.getTransferData();
|
||||
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(transform.getBackgroundImageUrl(),
|
||||
null,
|
||||
resourceImage.getTag(),
|
||||
@@ -98,9 +99,9 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
bgImage.getHeight(),
|
||||
null,
|
||||
null,
|
||||
randomX,
|
||||
data.x,
|
||||
CaptchaTypeConstant.CONCAT);
|
||||
imageCaptchaInfo.setData(randomY);
|
||||
customData.putViewData("randomY", data.y);
|
||||
imageCaptchaInfo.setTolerant(0.05F);
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
|
||||
+78
-52
@@ -1,13 +1,11 @@
|
||||
package cloud.tianai.captcha.generator.impl;
|
||||
|
||||
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.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.RotateImageCaptchaInfo;
|
||||
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;
|
||||
@@ -19,13 +17,12 @@ import lombok.SneakyThrows;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
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_MASK_IMAGE_NAME;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
@@ -53,71 +50,98 @@ 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")));
|
||||
resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
|
||||
// 添加一些系统的 模板文件
|
||||
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png")));
|
||||
template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
|
||||
template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/matrix.png")));
|
||||
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")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1);
|
||||
|
||||
ResourceMap template2 = new ResourceMap(CommonConstant.DEFAULT_TAG + "_obfuscate", 4);
|
||||
template2.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/4/active.png")));
|
||||
template2.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/4/fixed.png")));
|
||||
template2.put(TEMPLATE_MASK_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/4/mask.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
|
||||
// 旋转验证码没有混淆
|
||||
ResourceMap templateImages = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
|
||||
Collection<InputStream> inputStreams = new LinkedList<>();
|
||||
try {
|
||||
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
|
||||
GenerateParam param = transferData.getParam();
|
||||
CustomData data = new CustomData();
|
||||
ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
|
||||
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
|
||||
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
|
||||
inputStreams.add(resourceInputStream);
|
||||
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
||||
// 拷贝一份图片
|
||||
BufferedImage targetBackground = CaptchaImageUtils.copyImage(cutBackground, cutBackground.getType());
|
||||
BufferedImage background = getResourceImage(resourceImage);
|
||||
|
||||
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
||||
inputStreams.add(fixedTemplateInput);
|
||||
BufferedImage fixedTemplate = CaptchaImageUtils.wrapFile2BufferedImage(fixedTemplateInput);
|
||||
|
||||
InputStream activeTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME);
|
||||
inputStreams.add(activeTemplateInput);
|
||||
BufferedImage activeTemplate = CaptchaImageUtils.wrapFile2BufferedImage(activeTemplateInput);
|
||||
|
||||
InputStream matrixTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME);
|
||||
inputStreams.add(matrixTemplateInput);
|
||||
BufferedImage matrixTemplate = CaptchaImageUtils.wrapFile2BufferedImage(matrixTemplateInput);
|
||||
BufferedImage fixedTemplate = getTemplateImage(templateResource, TEMPLATE_FIXED_IMAGE_NAME);
|
||||
BufferedImage activeTemplate = getTemplateImage(templateResource, TEMPLATE_ACTIVE_IMAGE_NAME);
|
||||
BufferedImage maskTemplate = fixedTemplate;
|
||||
Optional<BufferedImage> maskTemplateOptional = getTemplateImageOfOptional(templateResource, TEMPLATE_MASK_IMAGE_NAME);
|
||||
if (maskTemplateOptional.isPresent()) {
|
||||
maskTemplate = maskTemplateOptional.get();
|
||||
}
|
||||
|
||||
// 算出居中的x和y
|
||||
int x = targetBackground.getWidth() / 2 - fixedTemplate.getWidth() / 2;
|
||||
int y = targetBackground.getHeight() / 2 - fixedTemplate.getHeight() / 2;
|
||||
CaptchaImageUtils.overlayImage(targetBackground, fixedTemplate, x, y);
|
||||
int x = background.getWidth() / 2 - fixedTemplate.getWidth() / 2;
|
||||
int y = background.getHeight() / 2 - fixedTemplate.getHeight() / 2;
|
||||
|
||||
// 抠图部分
|
||||
BufferedImage cutImage = CaptchaImageUtils.cutImage(cutBackground, fixedTemplate, x, y);
|
||||
CaptchaImageUtils.overlayImage(cutImage, activeTemplate, 0, 0);
|
||||
BufferedImage cutImage = CaptchaImageUtils.cutImage(background, maskTemplate, x, y);
|
||||
BufferedImage rotateFixed = fixedTemplate;
|
||||
BufferedImage rotateActive = activeTemplate;
|
||||
if (param.getObfuscate()) {
|
||||
int randomDegree = randomInt(10, 350);
|
||||
rotateFixed = CaptchaImageUtils.rotateImage(fixedTemplate, randomDegree);
|
||||
randomDegree = randomInt(10, 350);
|
||||
rotateActive = CaptchaImageUtils.rotateImage(activeTemplate, randomDegree);
|
||||
}
|
||||
CaptchaImageUtils.overlayImage(background, rotateFixed, x, y);
|
||||
CaptchaImageUtils.overlayImage(cutImage, rotateActive, 0, 0);
|
||||
// 随机旋转抠图部分
|
||||
// 随机x, 转换为角度
|
||||
int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 10, targetBackground.getWidth() - 10);
|
||||
double degree = 360d - randomX / ((targetBackground.getWidth()) / 360d);
|
||||
int randomX = randomInt(fixedTemplate.getWidth() + 10, background.getWidth() - 10);
|
||||
double degree = 360d - randomX / ((background.getWidth()) / 360d);
|
||||
// 旋转的透明图片是一张正方形的
|
||||
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(background.getHeight(), background.getHeight());
|
||||
CaptchaImageUtils.centerOverlayAndRotateImage(matrixTemplate, cutImage, degree);
|
||||
return wrapRotateCaptchaInfo(degree, randomX, targetBackground, matrixTemplate, param, templateImages, resourceImage);
|
||||
} finally {
|
||||
// 使用完后关闭流
|
||||
for (InputStream inputStream : inputStreams) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
|
||||
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);
|
||||
|
||||
// return wrapRotateCaptchaInfo(degree, randomX, background, matrixTemplate, param, templateResource, resourceImage, data);
|
||||
}
|
||||
|
||||
public static class RotateData {
|
||||
double degree;
|
||||
int randomX;
|
||||
}
|
||||
|
||||
private String getObfuscateTag(String templateTag) {
|
||||
if (templateTag == null) {
|
||||
return "obfuscate";
|
||||
}
|
||||
return templateTag + "_" + "obfuscate";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private ImageCaptchaInfo wrapRotateCaptchaInfo(double degree, int randomX, BufferedImage backgroundImage, BufferedImage sliderImage, GenerateParam param, ResourceMap templateResource, Resource resourceImage) {
|
||||
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource);
|
||||
return RotateImageCaptchaInfo.of(degree,
|
||||
randomX,
|
||||
@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();
|
||||
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, data);
|
||||
RotateImageCaptchaInfo imageCaptchaInfo = RotateImageCaptchaInfo.of(rotateData.degree,
|
||||
rotateData.randomX,
|
||||
transform.getBackgroundImageUrl(),
|
||||
transform.getTemplateImageUrl(),
|
||||
resourceImage.getTag(),
|
||||
@@ -125,6 +149,8 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
backgroundImage.getWidth(), backgroundImage.getHeight(),
|
||||
sliderImage.getWidth(), sliderImage.getHeight()
|
||||
);
|
||||
imageCaptchaInfo.setData(data);
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+87
-80
@@ -4,10 +4,7 @@ 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.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.SliderImageCaptchaInfo;
|
||||
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;
|
||||
@@ -17,16 +14,16 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
@@ -63,80 +60,91 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
|
||||
public void doGenerateCaptchaImage(CaptchaTransferData transferData) {
|
||||
GenerateParam param = transferData.getParam();
|
||||
Boolean obfuscate = param.getObfuscate();
|
||||
ResourceMap templateImages = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
|
||||
Collection<InputStream> inputStreams = new LinkedList<>();
|
||||
try {
|
||||
ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
|
||||
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
|
||||
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
|
||||
inputStreams.add(resourceInputStream);
|
||||
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
||||
// 拷贝一份图片
|
||||
BufferedImage targetBackground = CaptchaImageUtils.copyImage(cutBackground, cutBackground.getType());
|
||||
|
||||
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
||||
inputStreams.add(fixedTemplateInput);
|
||||
BufferedImage fixedTemplate = CaptchaImageUtils.wrapFile2BufferedImage(fixedTemplateInput);
|
||||
|
||||
InputStream activeTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME);
|
||||
inputStreams.add(activeTemplateInput);
|
||||
BufferedImage activeTemplate = CaptchaImageUtils.wrapFile2BufferedImage(activeTemplateInput);
|
||||
|
||||
|
||||
InputStream matrixTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME);
|
||||
inputStreams.add(matrixTemplateInput);
|
||||
BufferedImage matrixTemplate = CaptchaImageUtils.wrapFile2BufferedImage(matrixTemplateInput);
|
||||
|
||||
// BufferedImage cutTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, CUT_IMAGE_NAME));
|
||||
|
||||
BufferedImage background = getResourceImage(resourceImage);
|
||||
BufferedImage fixedTemplate = getTemplateImage(templateResource, TEMPLATE_FIXED_IMAGE_NAME);
|
||||
BufferedImage activeTemplate = getTemplateImage(templateResource, TEMPLATE_ACTIVE_IMAGE_NAME);
|
||||
BufferedImage maskTemplate = fixedTemplate;
|
||||
Optional<BufferedImage> maskTemplateOptional = getTemplateImageOfOptional(templateResource, TEMPLATE_MASK_IMAGE_NAME);
|
||||
if (maskTemplateOptional.isPresent()) {
|
||||
maskTemplate = maskTemplateOptional.get();
|
||||
}
|
||||
// 获取随机的 x 和 y 轴
|
||||
int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 5, targetBackground.getWidth() - fixedTemplate.getWidth() - 10);
|
||||
int randomY = ThreadLocalRandom.current().nextInt(targetBackground.getHeight() - fixedTemplate.getHeight());
|
||||
int randomX = randomInt(fixedTemplate.getWidth() + 5, background.getWidth() - fixedTemplate.getWidth() - 10);
|
||||
int randomY = randomInt(background.getHeight() - fixedTemplate.getHeight());
|
||||
|
||||
CaptchaImageUtils.overlayImage(targetBackground, fixedTemplate, randomX, randomY);
|
||||
BufferedImage cutImage = CaptchaImageUtils.cutImage(background, maskTemplate, randomX, randomY);
|
||||
CaptchaImageUtils.overlayImage(background, fixedTemplate, randomX, randomY);
|
||||
if (obfuscate) {
|
||||
// 加入混淆滑块
|
||||
int obfuscateX = randomObfuscateX(randomX, fixedTemplate.getWidth(), targetBackground.getWidth());
|
||||
CaptchaImageUtils.overlayImage(targetBackground, fixedTemplate, obfuscateX, randomY);
|
||||
Optional<BufferedImage> obfuscateFixedTemplate = getTemplateImageOfOptional(templateResource, OBFUSCATE_TEMPLATE_FIXED_IMAGE_NAME);
|
||||
BufferedImage obfuscateImage = obfuscateFixedTemplate.orElseGet(() -> createObfuscate(fixedTemplate));
|
||||
int obfuscateX = randomObfuscateX(randomX, fixedTemplate.getWidth(), background.getWidth());
|
||||
CaptchaImageUtils.overlayImage(background, obfuscateImage, obfuscateX, randomY);
|
||||
}
|
||||
BufferedImage cutImage = CaptchaImageUtils.cutImage(cutBackground, fixedTemplate, randomX, randomY);
|
||||
CaptchaImageUtils.overlayImage(cutImage, activeTemplate, 0, 0);
|
||||
// 这里创建一张png透明图片
|
||||
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(activeTemplate.getWidth(), background.getHeight());
|
||||
CaptchaImageUtils.overlayImage(matrixTemplate, cutImage, 0, randomY);
|
||||
return wrapSliderCaptchaInfo(randomX, randomY, targetBackground, matrixTemplate, param, templateImages, resourceImage);
|
||||
} finally {
|
||||
// 使用完后关闭流
|
||||
for (InputStream inputStream : inputStreams) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// 后处理
|
||||
// applyPostProcessorBeforeWrapImageCaptchaInfo(transferData, this);
|
||||
// imageCaptchaInfo = wrapSliderCaptchaInfo(randomX, randomY, transferData);
|
||||
// applyPostProcessorAfterGenerateCaptchaImage(transferData, imageCaptchaInfo, this);
|
||||
// return imageCaptchaInfo;
|
||||
}
|
||||
|
||||
protected BufferedImage createObfuscate(BufferedImage fixedImage) {
|
||||
// 随机拉伸或缩放宽高, 每次只拉伸高或者宽
|
||||
int width = fixedImage.getWidth();
|
||||
int height = fixedImage.getHeight();
|
||||
int window = randomInt(-3, 4);
|
||||
if (randomBoolean()) {
|
||||
height = height + window * 5;
|
||||
} else {
|
||||
width = width + window * 5;
|
||||
}
|
||||
int type = fixedImage.getColorModel().getTransparency();
|
||||
BufferedImage image = new BufferedImage(width, height, type);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
// 透明度
|
||||
double alpha = ThreadLocalRandom.current().nextDouble(0.5, 0.8);
|
||||
AlphaComposite alphaComposite = AlphaComposite.Src.derive((float) alpha);
|
||||
graphics.setComposite(alphaComposite);
|
||||
graphics.drawImage(fixedImage, 0, 0, width, height, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
public static class XandY {
|
||||
int x;
|
||||
int y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装成 SliderCaptchaInfo
|
||||
*
|
||||
* @param randomX 随机生成的 x轴
|
||||
* @param randomY 随机生成的 y轴
|
||||
* @param backgroundImage 背景图片
|
||||
* @param sliderImage 滑块图片
|
||||
* @param param 接口传入参数
|
||||
* @return SliderCaptchaInfo
|
||||
*/
|
||||
@SneakyThrows
|
||||
public SliderImageCaptchaInfo wrapSliderCaptchaInfo(int randomX,
|
||||
int randomY,
|
||||
BufferedImage backgroundImage,
|
||||
BufferedImage sliderImage,
|
||||
GenerateParam param,
|
||||
ResourceMap templateResource,
|
||||
Resource resourceImage) {
|
||||
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource);
|
||||
@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();
|
||||
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData);
|
||||
|
||||
return SliderImageCaptchaInfo.of(randomX, randomY,
|
||||
SliderImageCaptchaInfo imageCaptchaInfo = SliderImageCaptchaInfo.of(data.x, data.y,
|
||||
transform.getBackgroundImageUrl(),
|
||||
transform.getTemplateImageUrl(),
|
||||
resourceImage.getTag(),
|
||||
@@ -144,15 +152,17 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
backgroundImage.getWidth(), backgroundImage.getHeight(),
|
||||
sliderImage.getWidth(), sliderImage.getHeight()
|
||||
);
|
||||
imageCaptchaInfo.setData(customData);
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
|
||||
protected int randomObfuscateX(int sliderX, int slWidth, int bgWidth) {
|
||||
if (bgWidth / 2 > (sliderX + (slWidth / 2))) {
|
||||
// 右边混淆
|
||||
return ThreadLocalRandom.current().nextInt(sliderX + slWidth, bgWidth - slWidth);
|
||||
return randomInt(sliderX + slWidth, bgWidth - slWidth);
|
||||
}
|
||||
// 左边混淆
|
||||
return ThreadLocalRandom.current().nextInt(slWidth, sliderX - slWidth);
|
||||
return randomInt(slWidth, sliderX - slWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,20 +171,17 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
public void initDefaultResource() {
|
||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||
// 添加一些系统的资源文件
|
||||
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
|
||||
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
|
||||
// 添加一些系统的 模板文件
|
||||
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png")));
|
||||
template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png")));
|
||||
template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/matrix.png")));
|
||||
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png")));
|
||||
template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template1);
|
||||
|
||||
|
||||
ResourceMap template2 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template2.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png")));
|
||||
template2.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png")));
|
||||
template2.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/matrix.png")));
|
||||
template2.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png")));
|
||||
template2.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template2);
|
||||
}
|
||||
}
|
||||
|
||||
+49
-31
@@ -1,20 +1,16 @@
|
||||
package cloud.tianai.captcha.generator.impl;
|
||||
|
||||
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.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.model.dto.ImageTransformData;
|
||||
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.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -26,23 +22,25 @@ import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/4/27 11:46
|
||||
* @Description 点选验证码
|
||||
*/
|
||||
public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator {
|
||||
public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCaptchaGenerator {
|
||||
|
||||
/** 字体包. */
|
||||
@Getter
|
||||
@Setter
|
||||
protected Font font;
|
||||
protected List<FontWrapper> fonts = new ArrayList<>();
|
||||
@Getter
|
||||
@Setter
|
||||
protected Integer clickImgWidth = 80;
|
||||
protected Integer clickImgWidth = 100;
|
||||
@Getter
|
||||
@Setter
|
||||
protected Integer clickImgHeight = 80;
|
||||
protected Integer clickImgHeight = 100;
|
||||
@Getter
|
||||
@Setter
|
||||
protected int tipImageInterferenceLineNum = 2;
|
||||
@@ -63,19 +61,25 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
||||
* 方案: 通过 线性回归模型 计算出 intercept和coef 用于计算 容错值
|
||||
* 训练数据为 宋体 字体大小为 30~150 随机选择7组数据进行训练, 训练后r2结果为 0.9967106324620846
|
||||
*/
|
||||
protected float intercept = 0.39583333f;
|
||||
protected float coef = 0.14645833f;
|
||||
|
||||
protected float currentFontTopCoef = 0.0f;
|
||||
|
||||
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
|
||||
// protected float intercept = 0.39583333f;
|
||||
// protected float coef = 0.14645833f;
|
||||
//
|
||||
// protected float currentFontTopCoef = 0.0f;
|
||||
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
|
||||
super(imageCaptchaResourceManager);
|
||||
}
|
||||
|
||||
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform) {
|
||||
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform) {
|
||||
super(imageCaptchaResourceManager);
|
||||
setImageTransform(imageTransform);
|
||||
}
|
||||
|
||||
public StandardWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform, List<FontWrapper> fonts) {
|
||||
super(imageCaptchaResourceManager);
|
||||
setImageTransform(imageTransform);
|
||||
this.fonts = fonts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Resource> randomGetClickImgTips(GenerateParam param) {
|
||||
int tipSize = interferenceCount + checkClickCount;
|
||||
@@ -92,32 +96,31 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
||||
@Override
|
||||
@SneakyThrows({IOException.class, FontFormatException.class})
|
||||
protected void doInit(boolean initDefaultResource) {
|
||||
if (this.font == null) {
|
||||
if (CollectionUtils.isEmpty(fonts)) {
|
||||
// 使用默认字体
|
||||
Resource fontResource = new Resource(null, "META-INF/fonts/SIMSUN.TTC");
|
||||
InputStream inputStream = new ClassPathResourceProvider().doGetResourceInputStream(fontResource);
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
|
||||
this.font = font.deriveFont(Font.BOLD, 70);
|
||||
font = font.deriveFont(Font.BOLD, 70);
|
||||
float currentFontTopCoef = 0.14645833f * font.getSize() + 0.39583333f;
|
||||
fonts.add(new FontWrapper(font, currentFontTopCoef));
|
||||
}
|
||||
// 计算容错
|
||||
currentFontTopCoef = coef * font.getSize() + intercept;
|
||||
if (initDefaultResource) {
|
||||
initDefaultResource();
|
||||
}
|
||||
}
|
||||
|
||||
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, Font font) {
|
||||
super(imageCaptchaResourceManager);
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
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")));
|
||||
resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
}
|
||||
|
||||
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
|
||||
FontWrapper fontWrapper = fonts.get(randomInt(fonts.size()));
|
||||
Font font = fontWrapper.getFont();
|
||||
float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef();
|
||||
String tips = imageCheckDefinitions.stream().map(c -> c.getTip().getData()).collect(Collectors.joining());
|
||||
// 生成随机颜色
|
||||
int fontWidth = tips.length() * font.getSize();
|
||||
@@ -137,6 +140,9 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
||||
Color randomColor = CaptchaImageUtils.getRandomColor(random);
|
||||
// 随机角度
|
||||
int randomDeg = randomInt(0, 85);
|
||||
FontWrapper fontWrapper = fonts.get(randomInt(fonts.size()));
|
||||
Font font = fontWrapper.getFont();
|
||||
float currentFontTopCoef = fontWrapper.getCurrentFontTopCoef();
|
||||
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
|
||||
tip.getData(),
|
||||
font,
|
||||
@@ -161,11 +167,15 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
|
||||
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList, Resource resourceImage) {
|
||||
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();
|
||||
// 提示图片
|
||||
BufferedImage tipImage = genTipImage(checkClickImageCheckDefinitionList).getImage();
|
||||
ImageTransformData transform = getImageTransform().transform(param, bgImage, tipImage, resourceImage, checkClickImageCheckDefinitionList);
|
||||
ImageTransformData transform = getImageTransform().transform(param, bgImage, tipImage, resourceImage, checkClickImageCheckDefinitionList, data);
|
||||
ImageCaptchaInfo clickImageCaptchaInfo = new ImageCaptchaInfo();
|
||||
clickImageCaptchaInfo.setBackgroundImage(transform.getBackgroundImageUrl());
|
||||
clickImageCaptchaInfo.setBackgroundImageTag(resourceImage.getTag());
|
||||
@@ -177,8 +187,16 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
||||
clickImageCaptchaInfo.setRandomX(null);
|
||||
clickImageCaptchaInfo.setTolerant(null);
|
||||
clickImageCaptchaInfo.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
|
||||
clickImageCaptchaInfo.setExpand(checkClickImageCheckDefinitionList);
|
||||
data.setExpand(checkClickImageCheckDefinitionList);
|
||||
return clickImageCaptchaInfo;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class FontWrapper {
|
||||
private Font font;
|
||||
private float currentFontTopCoef;
|
||||
}
|
||||
}
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
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.ImageCaptchaInfo;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/4/24 15:23
|
||||
* @Description 验证码后处理器管理
|
||||
*/
|
||||
public class StaticCaptchaPostProcessorManager {
|
||||
|
||||
@Getter
|
||||
private static LinkedList<ImageCaptchaPostProcessor> processors = new LinkedList<>();
|
||||
|
||||
static {
|
||||
|
||||
}
|
||||
|
||||
public static void add(ImageCaptchaPostProcessor processor) {
|
||||
processors.add(processor);
|
||||
}
|
||||
|
||||
public static void add(Integer index, ImageCaptchaPostProcessor processor) {
|
||||
processors.add(index, processor);
|
||||
}
|
||||
|
||||
public static void addFirst(ImageCaptchaPostProcessor processor) {
|
||||
processors.addFirst(processor);
|
||||
}
|
||||
|
||||
public static void addLast(ImageCaptchaPostProcessor processor) {
|
||||
processors.addLast(processor);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
processors.clear();
|
||||
}
|
||||
|
||||
public static void add(List<ImageCaptchaPostProcessor> addPostProcessors) {
|
||||
processors.addAll(addPostProcessors);
|
||||
}
|
||||
|
||||
|
||||
public static ImageCaptchaInfo applyPostProcessorBeforeGenerate(CaptchaTransferData transferData, ImageCaptchaGenerator context) {
|
||||
for (ImageCaptchaPostProcessor processor : processors) {
|
||||
try {
|
||||
ImageCaptchaInfo imageCaptchaInfo = processor.beforeGenerateCaptchaImage(transferData, context);
|
||||
if (imageCaptchaInfo != null) {
|
||||
return imageCaptchaInfo;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.beforeGenerateCaptchaImage error, [" + processor.getClass() + "]", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void applyPostProcessorBeforeWrapImageCaptchaInfo(CaptchaTransferData transferData, ImageCaptchaGenerator context) {
|
||||
for (ImageCaptchaPostProcessor processor : processors) {
|
||||
try {
|
||||
processor.beforeWrapImageCaptchaInfo(transferData, context);
|
||||
} catch (Exception e) {
|
||||
throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.beforeWrapImageCaptchaInfo error, [" + processor.getClass() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void applyPostProcessorAfterGenerateCaptchaImage(CaptchaTransferData transferData, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator context) {
|
||||
for (ImageCaptchaPostProcessor processor : processors) {
|
||||
try {
|
||||
processor.afterGenerateCaptchaImage(transferData, imageCaptchaInfo, context);
|
||||
} catch (Exception e) {
|
||||
throw new ImageCaptchaException("apply ImageCaptchaPostProcessor.afterGenerateCaptchaImage error, [" + processor.getClass() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -4,13 +4,13 @@ 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.StandardRandomWordClickImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.impl.StandardWordClickImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
|
||||
public class StandardRandomWordClickImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider {
|
||||
public class StandardWordClickImageCaptchaGeneratorProvider implements ImageCaptchaGeneratorProvider {
|
||||
@Override
|
||||
public ImageCaptchaGenerator get(ImageCaptchaResourceManager resourceManager, ImageTransform imageTransform) {
|
||||
return new StandardRandomWordClickImageCaptchaGenerator(resourceManager, imageTransform);
|
||||
return new StandardWordClickImageCaptchaGenerator(resourceManager, imageTransform);
|
||||
}
|
||||
|
||||
@Override
|
||||
+7
-1
@@ -1,6 +1,7 @@
|
||||
package cloud.tianai.captcha.generator.impl.transform;
|
||||
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
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.ImageTransformData;
|
||||
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
||||
@@ -59,7 +60,12 @@ public class Base64ImageTransform implements ImageTransform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, BufferedImage templateImage, Object backgroundResource, Object templateResource) {
|
||||
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()));
|
||||
|
||||
@@ -102,12 +102,12 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
ImageCaptchaInfo imageCaptchaInfo) {
|
||||
// type
|
||||
String type = (String) map.getOrDefault(TYPE_KEY, CaptchaTypeConstant.SLIDER);
|
||||
Object expand = imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getExpand();
|
||||
if (CaptchaUtils.isSliderCaptcha(type)) {
|
||||
// 滑动验证码
|
||||
addPercentage(imageCaptchaInfo, map);
|
||||
} else if (CaptchaUtils.isClickCaptcha(type)) {
|
||||
// 图片点选验证码
|
||||
Object expand = imageCaptchaInfo.getExpand();
|
||||
if (expand == null) {
|
||||
throw new IllegalArgumentException("点选验证码扩展数据转换为 List<ClickImageCheckDefinition> 失败, info=" + imageCaptchaInfo);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class Test3 {
|
||||
// 生成 具有混淆的 滑块验证码 (目前只有滑块验证码支持混淆滑块, 旋转验证,滑动还原,点选验证 均不支持混淆功能)
|
||||
ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(GenerateParam.builder()
|
||||
.type(CaptchaTypeConstant.SLIDER)
|
||||
.sliderFormatName("jpeg")
|
||||
.templateFormatName("jpeg")
|
||||
.backgroundFormatName("png")
|
||||
// 是否添加混淆滑块
|
||||
.obfuscate(true)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class Test4 {
|
||||
// 参考 https://bitbucket.org/luciad/webp-imageio
|
||||
ImageCaptchaInfo slideImageInfo = imageCaptchaGenerator.generateCaptchaImage(GenerateParam.builder()
|
||||
.type(CaptchaTypeConstant.ROTATE)
|
||||
.sliderFormatName("webp")
|
||||
.templateFormatName("webp")
|
||||
.backgroundFormatName("webp")
|
||||
.build());
|
||||
System.out.println(slideImageInfo);
|
||||
|
||||
@@ -24,7 +24,7 @@ public class Test8 {
|
||||
// 获取背景图片的base64
|
||||
String backgroundImage = slideImageInfo.getBackgroundImage();
|
||||
// 获取滑块图片
|
||||
String sliderImage = slideImageInfo.getSliderImage();
|
||||
String sliderImage = slideImageInfo.getTemplateImage();
|
||||
System.out.println(slideImageInfo);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user