diff --git a/readme.md b/readme.md index 06f35a5..4631b98 100644 --- a/readme.md +++ b/readme.md @@ -373,7 +373,7 @@ public class Test8 { // 参数四: 检查时间间隔 ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager(); ImageTransform imageTransform = new Base64ImageTransform(); - ImageCaptchaGenerator imageCaptchaGenerator = new CacheImageCaptchaGenerator(new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform), 10, 1000, 100); + ImageCaptchaGenerator imageCaptchaGenerator = new CacheImageCaptchaGenerator(new MultiImageCaptchaGenerator(imageCaptchaResourceManager, imageTransform), 10, 1000, 100); imageCaptchaGenerator.init(true); // 生成滑块图片 ImageCaptchaInfo slideImageInfo = imageCaptchaGenerator.generateCaptchaImage(CaptchaTypeConstant.SLIDER); diff --git a/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java index 5bef908..5092920 100644 --- a/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java @@ -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() { diff --git a/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaPostProcessor.java b/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaPostProcessor.java new file mode 100644 index 0000000..8174f0c --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaPostProcessor.java @@ -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) { + + } +} diff --git a/src/main/java/cloud/tianai/captcha/generator/ImageTransform.java b/src/main/java/cloud/tianai/captcha/generator/ImageTransform.java index e9176a3..e748766 100644 --- a/src/main/java/cloud/tianai/captcha/generator/ImageTransform.java +++ b/src/main/java/cloud/tianai/captcha/generator/ImageTransform.java @@ -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); } diff --git a/src/main/java/cloud/tianai/captcha/generator/common/constant/SliderCaptchaConstant.java b/src/main/java/cloud/tianai/captcha/generator/common/constant/SliderCaptchaConstant.java index 246a78c..46b0a58 100644 --- a/src/main/java/cloud/tianai/captcha/generator/common/constant/SliderCaptchaConstant.java +++ b/src/main/java/cloud/tianai/captcha/generator/common/constant/SliderCaptchaConstant.java @@ -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; } diff --git a/src/main/java/cloud/tianai/captcha/generator/common/model/dto/CaptchaTransferData.java b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/CaptchaTransferData.java new file mode 100644 index 0000000..80d418d --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/CaptchaTransferData.java @@ -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); + } +} diff --git a/src/main/java/cloud/tianai/captcha/generator/common/model/dto/CustomData.java b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/CustomData.java new file mode 100644 index 0000000..a31c7eb --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/CustomData.java @@ -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 viewData; + /** 内部使用的字段数据. */ + private Map 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); + } +} diff --git a/src/main/java/cloud/tianai/captcha/generator/common/model/dto/ImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/ImageCaptchaInfo.java index 04cff2d..f3a5376 100644 --- a/src/main/java/cloud/tianai/captcha/generator/common/model/dto/ImageCaptchaInfo.java +++ b/src/main/java/cloud/tianai/captcha/generator/common/model/dto/ImageCaptchaInfo.java @@ -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, diff --git a/src/main/java/cloud/tianai/captcha/generator/common/util/CaptchaImageUtils.java b/src/main/java/cloud/tianai/captcha/generator/common/util/CaptchaImageUtils.java index 0b332c0..146e993 100644 --- a/src/main/java/cloud/tianai/captcha/generator/common/util/CaptchaImageUtils.java +++ b/src/main/java/cloud/tianai/captcha/generator/common/util/CaptchaImageUtils.java @@ -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 * diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java index cf1fb17..4013983 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java @@ -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 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 checkClickImageCheckDefinitionList, Resource resourceImage); - /** * @Author: 天爱有情 * @date 2022/4/28 14:26 diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java index 0163c17..29b8450 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java @@ -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; } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java index 9576b3e..0901437 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java @@ -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,48 +47,50 @@ 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 inputStreams = new LinkedList<>(); - try { - Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); - InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage); - inputStreams.add(resourceInputStream); - BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream); - int spacingY = bgImage.getHeight() / 4; - int randomY = ThreadLocalRandom.current().nextInt(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); - BufferedImage[] bgImageTopSplit = splitImage(randomX, false, bgImageSplit[0]); + Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); + BufferedImage bgImage = getResourceImage(resourceImage); + int spacingY = bgImage.getHeight() / 4; + int randomY = randomInt(spacingY, bgImage.getHeight() - spacingY); + BufferedImage[] bgImageSplit = splitImage(randomY, true, bgImage); + int spacingX = bgImage.getWidth() / 8; + int randomX = randomInt(spacingX, bgImage.getWidth() - bgImage.getWidth() / 5); + BufferedImage[] bgImageTopSplit = splitImage(randomX, false, bgImageSplit[0]); - BufferedImage sliderImage = concatImage(true, - bgImageTopSplit[0].getWidth() - + 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 - } - } - } + BufferedImage sliderImage = concatImage(true, + bgImageTopSplit[0].getWidth() + + bgImageTopSplit[1].getWidth(), bgImageTopSplit[0].getHeight(), bgImageTopSplit[1], bgImageTopSplit[0]); + bgImage = concatImage(false, bgImageSplit[1].getWidth(), sliderImage.getHeight() + bgImageSplit[1].getHeight(), + sliderImage, bgImageSplit[1]); + 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; } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java index b826770..c3009d8 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java @@ -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 inputStreams = new LinkedList<>(); - try { - 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()); + 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()); + 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); - - // 算出居中的x和y - int x = targetBackground.getWidth() / 2 - fixedTemplate.getWidth() / 2; - int y = targetBackground.getHeight() / 2 - fixedTemplate.getHeight() / 2; - CaptchaImageUtils.overlayImage(targetBackground, fixedTemplate, x, y); - // 抠图部分 - BufferedImage cutImage = CaptchaImageUtils.cutImage(cutBackground, fixedTemplate, x, y); - CaptchaImageUtils.overlayImage(cutImage, activeTemplate, 0, 0); - // 随机旋转抠图部分 - // 随机x, 转换为角度 - int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 10, targetBackground.getWidth() - 10); - double degree = 360d - randomX / ((targetBackground.getWidth()) / 360d); - 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 - } - } + BufferedImage fixedTemplate = getTemplateImage(templateResource, TEMPLATE_FIXED_IMAGE_NAME); + BufferedImage activeTemplate = getTemplateImage(templateResource, TEMPLATE_ACTIVE_IMAGE_NAME); + BufferedImage maskTemplate = fixedTemplate; + Optional maskTemplateOptional = getTemplateImageOfOptional(templateResource, TEMPLATE_MASK_IMAGE_NAME); + if (maskTemplateOptional.isPresent()) { + maskTemplate = maskTemplateOptional.get(); } + + // 算出居中的x和y + int x = background.getWidth() / 2 - fixedTemplate.getWidth() / 2; + int y = background.getHeight() / 2 - fixedTemplate.getHeight() / 2; + + // 抠图部分 + 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 = 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); + + 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; } } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java index 1ae1cf4..f6ea0e8 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java @@ -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 inputStreams = new LinkedList<>(); - try { - 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)); - - // 获取随机的 x 和 y 轴 - int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 5, targetBackground.getWidth() - fixedTemplate.getWidth() - 10); - int randomY = ThreadLocalRandom.current().nextInt(targetBackground.getHeight() - fixedTemplate.getHeight()); - - CaptchaImageUtils.overlayImage(targetBackground, fixedTemplate, randomX, randomY); - if (obfuscate) { - // 加入混淆滑块 - int obfuscateX = randomObfuscateX(randomX, fixedTemplate.getWidth(), targetBackground.getWidth()); - CaptchaImageUtils.overlayImage(targetBackground, fixedTemplate, obfuscateX, randomY); - } - BufferedImage cutImage = CaptchaImageUtils.cutImage(cutBackground, fixedTemplate, randomX, randomY); - CaptchaImageUtils.overlayImage(cutImage, activeTemplate, 0, 0); - 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 - } - } + ResourceMap templateResource = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag()); + Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag()); + BufferedImage background = getResourceImage(resourceImage); + BufferedImage fixedTemplate = getTemplateImage(templateResource, TEMPLATE_FIXED_IMAGE_NAME); + BufferedImage activeTemplate = getTemplateImage(templateResource, TEMPLATE_ACTIVE_IMAGE_NAME); + BufferedImage maskTemplate = fixedTemplate; + Optional maskTemplateOptional = getTemplateImageOfOptional(templateResource, TEMPLATE_MASK_IMAGE_NAME); + if (maskTemplateOptional.isPresent()) { + maskTemplate = maskTemplateOptional.get(); } + // 获取随机的 x 和 y 轴 + int randomX = randomInt(fixedTemplate.getWidth() + 5, background.getWidth() - fixedTemplate.getWidth() - 10); + int randomY = randomInt(background.getHeight() - fixedTemplate.getHeight()); + + BufferedImage cutImage = CaptchaImageUtils.cutImage(background, maskTemplate, randomX, randomY); + CaptchaImageUtils.overlayImage(background, fixedTemplate, randomX, randomY); + if (obfuscate) { + Optional 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); + } + CaptchaImageUtils.overlayImage(cutImage, activeTemplate, 0, 0); + // 这里创建一张png透明图片 + BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(activeTemplate.getWidth(), background.getHeight()); + CaptchaImageUtils.overlayImage(matrixTemplate, cutImage, 0, randomY); + + 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; } - /** - * 包装成 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); + 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; + } - return SliderImageCaptchaInfo.of(randomX, randomY, + + public static class XandY { + int x; + int y; + } + + @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(); + ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource, customData); + + 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); } } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardRandomWordClickImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java similarity index 72% rename from src/main/java/cloud/tianai/captcha/generator/impl/StandardRandomWordClickImageCaptchaGenerator.java rename to src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java index 5414355..1787d4a 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardRandomWordClickImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java @@ -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 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 fonts) { + super(imageCaptchaResourceManager); + setImageTransform(imageTransform); + this.fonts = fonts; + } + @Override protected List 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 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 checkClickImageCheckDefinitionList, Resource resourceImage) { + public ImageCaptchaInfo doWrapImageCaptchaInfo(CaptchaTransferData transferData) { + List checkClickImageCheckDefinitionList = (List) 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; + } } + diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StaticCaptchaPostProcessorManager.java b/src/main/java/cloud/tianai/captcha/generator/impl/StaticCaptchaPostProcessorManager.java new file mode 100644 index 0000000..186f260 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StaticCaptchaPostProcessorManager.java @@ -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 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 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); + } + } + } + +} diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/provider/StandardRandomWordClickImageCaptchaGeneratorProvider.java b/src/main/java/cloud/tianai/captcha/generator/impl/provider/StandardWordClickImageCaptchaGeneratorProvider.java similarity index 67% rename from src/main/java/cloud/tianai/captcha/generator/impl/provider/StandardRandomWordClickImageCaptchaGeneratorProvider.java rename to src/main/java/cloud/tianai/captcha/generator/impl/provider/StandardWordClickImageCaptchaGeneratorProvider.java index bdc6598..57ad90d 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/provider/StandardRandomWordClickImageCaptchaGeneratorProvider.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/provider/StandardWordClickImageCaptchaGeneratorProvider.java @@ -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 diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/transform/Base64ImageTransform.java b/src/main/java/cloud/tianai/captcha/generator/impl/transform/Base64ImageTransform.java index eb293c6..a0f6b84 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/transform/Base64ImageTransform.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/transform/Base64ImageTransform.java @@ -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())); diff --git a/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java b/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java index 943c7f6..8b5da06 100644 --- a/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java +++ b/src/main/java/cloud/tianai/captcha/validator/impl/SimpleImageCaptchaValidator.java @@ -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 失败, info=" + imageCaptchaInfo); } diff --git a/src/main/test/java/example/readme/Test3.java b/src/main/test/java/example/readme/Test3.java index a60d43f..5a11b20 100644 --- a/src/main/test/java/example/readme/Test3.java +++ b/src/main/test/java/example/readme/Test3.java @@ -19,7 +19,7 @@ public class Test3 { // 生成 具有混淆的 滑块验证码 (目前只有滑块验证码支持混淆滑块, 旋转验证,滑动还原,点选验证 均不支持混淆功能) ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(GenerateParam.builder() .type(CaptchaTypeConstant.SLIDER) - .sliderFormatName("jpeg") + .templateFormatName("jpeg") .backgroundFormatName("png") // 是否添加混淆滑块 .obfuscate(true) diff --git a/src/main/test/java/example/readme/Test4.java b/src/main/test/java/example/readme/Test4.java index 8ee6dcd..752e6d1 100644 --- a/src/main/test/java/example/readme/Test4.java +++ b/src/main/test/java/example/readme/Test4.java @@ -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); diff --git a/src/main/test/java/example/readme/Test8.java b/src/main/test/java/example/readme/Test8.java index e416dd9..0e3aed0 100644 --- a/src/main/test/java/example/readme/Test8.java +++ b/src/main/test/java/example/readme/Test8.java @@ -24,7 +24,7 @@ public class Test8 { // 获取背景图片的base64 String backgroundImage = slideImageInfo.getBackgroundImage(); // 获取滑块图片 - String sliderImage = slideImageInfo.getSliderImage(); + String sliderImage = slideImageInfo.getTemplateImage(); System.out.println(slideImageInfo); } }