diff --git a/readme.md b/readme.md index 3d1389e..1be5526 100644 --- a/readme.md +++ b/readme.md @@ -34,6 +34,7 @@ ### 2. 使用 `SliderCaptchaGenerator`生成器生成滑块验证码 ```java +import cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.impl.StandardSliderCaptchaGenerator; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaResourceManager; @@ -41,9 +42,9 @@ import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaRe public class Test { public static void main(String[] args) throws InterruptedException { SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); - StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, true); + StandardImageCaptchaGenerator sliderCaptchaGenerator = new StandardImageCaptchaGenerator(sliderCaptchaResourceManager, true); // 生成滑块图片 - SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateSlideImageInfo(); + SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateCaptchaImage(); System.out.println(slideImageInfo); // 负责计算一些数据存到缓存中,用于校验使用 @@ -58,11 +59,12 @@ public class Test { ### 3. 使用`SliderCaptchaValidator`校验器 验证 ```java +import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator; public class Test2 { public static void main(String[] args) { - SliderCaptchaGenerator sliderCaptchaValidator = new BasicCaptchaTrackValidator(); + ImageCaptchaGenerator sliderCaptchaValidator = new BasicCaptchaTrackValidator(); // 用户传来的行为轨迹和进行校验 // - sliderCaptchaTrack为前端传来的滑动轨迹数据 diff --git a/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java b/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java index 1063893..dc3cc95 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java @@ -176,4 +176,43 @@ public class CaptchaImageUtils { } +// /** +// * 旋转图片 +// * +// * @param bufferedImage 图片 +// * @param degree 旋转xx度 +// */ +// public static void rotateImage(BufferedImage bufferedImage, int degree) { +// // 创建Graphics2D对象,用在底图对象上绘图 +// Graphics2D g2d = bufferedImage.createGraphics(); +// // 绘制 +// g2d.rotate(Math.toRadians(degree), bufferedImage.getWidth() / 2, bufferedImage.getHeight()/2); +// g2d.drawImage(bufferedImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null); +// // 释放图形上下文使用的系统资源 +// g2d.dispose(); +// } + + public static BufferedImage rotateImage(final BufferedImage bufferedimage, + final double degree) { + // 得到图片宽度。 + int w = bufferedimage.getWidth(); + // 得到图片高度。 + int h = bufferedimage.getHeight(); + // 得到图片透明度。 + int type = bufferedimage.getColorModel().getTransparency(); + BufferedImage img;// 空的图片。 + Graphics2D graphics2d;// 空的画笔。 + (graphics2d = (img = new BufferedImage(w, h, type)) + .createGraphics()).setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + // 旋转,degree是整型,度数,比如垂直90度。 + graphics2d.rotate(Math.toRadians(degree), w / 2, h / 2); + // 从bufferedimagecopy图片至img,0,0是img的坐标。 + graphics2d.drawImage(bufferedimage, 0, 0, null); + graphics2d.dispose(); + // 返回复制好的图片,原图片依然没有变,没有旋转,下次还可以使用。 + return img; + } + } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java new file mode 100644 index 0000000..9e69a35 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java @@ -0,0 +1,77 @@ +package cloud.tianai.captcha.template.slider.generator; + +import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Base64; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @Author: 天爱有情 + * @date 2022/4/22 16:30 + * @Description 抽象的验证码生成器 + */ +public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGenerator { + public static String DEFAULT_BG_IMAGE_TYPE = "jpeg"; + public static String DEFAULT_SLIDER_IMAGE_TYPE = "png"; + + @Getter + @Setter + /** 默认背景图片类型. */ + public String defaultBgImageType = DEFAULT_BG_IMAGE_TYPE; + @Getter + @Setter + /** 默认滑块图片类型. */ + public String defaultSliderImageType = DEFAULT_SLIDER_IMAGE_TYPE; + + @Override + public ImageCaptchaInfo generateCaptchaImage(String type) { + return generateCaptchaImage(type, defaultBgImageType, defaultSliderImageType); + } + + @SneakyThrows + @Override + public ImageCaptchaInfo generateCaptchaImage(String type, String backgroundFormatName, String sliderFormatName) { + return generateCaptchaImage(GenerateParam.builder() + .type(type) + .backgroundFormatName(backgroundFormatName) + .sliderFormatName(sliderFormatName) + .obfuscate(false) + .build()); + } + + /** + * 将图片转换成字符串格式 + * + * @param bufferedImage 图片 + * @param formatType 格式化类型 + * @return String + */ + public String transform(BufferedImage bufferedImage, String formatType) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ImageIO.write(bufferedImage, formatType, byteArrayOutputStream); + //转换成字节码 + byte[] data = byteArrayOutputStream.toByteArray(); + String base64 = Base64.getEncoder().encodeToString(data); + return "data:image/" + formatType + ";base64,".concat(base64); + } + + protected InputStream getTemplateFile(Map templateImages, String imageName) { + Resource resource = templateImages.get(imageName); + if (resource == null) { + throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName)); + } + return getSlideImageResourceManager().getResourceInputStream(resource); + } + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/SliderCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java similarity index 57% rename from src/main/java/cloud/tianai/captcha/template/slider/generator/SliderCaptchaGenerator.java rename to src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java index 2ab4ef2..d3489f3 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/SliderCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java @@ -1,33 +1,35 @@ package cloud.tianai.captcha.template.slider.generator; +import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; -import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator; /** * @Author: 天爱有情 * @date 2020/10/19 18:37 - * @Description 滑块验证码模板 + * @Description 图片验证码生成器 */ -public interface SliderCaptchaGenerator { +public interface ImageCaptchaGenerator { + /** - * 获取滑块验证码 - * + * 生成验证码图片 + * @param type 类型 {@link CaptchaTypeConstant} * @return SliderCaptchaInfo */ - SliderCaptchaInfo generateSlideImageInfo(); + ImageCaptchaInfo generateCaptchaImage(String type); /** * 生成滑块验证码 - * + * @param type type {@link CaptchaTypeConstant} * @param targetFormatName jpeg或者webp格式 * @param matrixFormatName png或者webp格式 * @return SliderCaptchaInfo */ - SliderCaptchaInfo generateSlideImageInfo(String targetFormatName, String matrixFormatName); + ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName); /** * 生成滑块验证码 @@ -35,20 +37,8 @@ public interface SliderCaptchaGenerator { * @param param 生成参数 * @return SliderCaptchaInfo */ - SliderCaptchaInfo generateSlideImageInfo(GenerateParam param); + ImageCaptchaInfo generateCaptchaImage(GenerateParam param); - /** - * 百分比对比 - * - * @param newPercentage 用户百分比 - * @param oriPercentage 原百分比 - * @return true 成功 false 失败 - *

- * 废除 , 建议使用 - * @see SliderCaptchaValidator 进行校验 - */ - @Deprecated - boolean percentageContrast(Float newPercentage, Float oriPercentage); /** * 获取滑块验证码资源管理器 diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java new file mode 100644 index 0000000..c50e1ce --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java @@ -0,0 +1,14 @@ +package cloud.tianai.captcha.template.slider.generator.common.constant; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 17:14 + * @Description 滑块类型 + */ +public interface CaptchaTypeConstant { + + /** 滑块. */ + String SLIDER = "SLIDER"; + /** 旋转. */ + String ROTATE = "ROTATE"; +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/GenerateParam.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/GenerateParam.java index 2382658..255bac7 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/GenerateParam.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/GenerateParam.java @@ -21,4 +21,6 @@ public class GenerateParam { private String sliderFormatName = "png"; /** 是否混淆.*/ private Boolean obfuscate = false; + /** 类型.*/ + private String type; } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java new file mode 100644 index 0000000..71711f6 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java @@ -0,0 +1,66 @@ +package cloud.tianai.captcha.template.slider.generator.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author: 天爱有情 + * @Date 2020/5/29 8:04 + * @Description 滑块验证码 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ImageCaptchaInfo { + + /** + * 背景图 + */ + private String backgroundImage; + /** + * 移动图 + */ + private String sliderImage; + /** 背景图片宽度. */ + private Integer bgImageWidth; + /** 背景图片高度. */ + private Integer bgImageHeight; + /** 滑块图片宽度. */ + private Integer sliderImageWidth; + /** 滑块图片高度. */ + private Integer sliderImageHeight; + /** 随机值.*/ + private Integer randomX; + /** + * 扩展字段 + */ + public Object expand; + + public ImageCaptchaInfo(String backgroundImage, + String sliderImage, + Integer bgImageWidth, + Integer bgImageHeight, + Integer sliderImageWidth, + Integer sliderImageHeight, + Integer randomX) { + this.backgroundImage = backgroundImage; + this.sliderImage = sliderImage; + this.bgImageWidth = bgImageWidth; + this.bgImageHeight = bgImageHeight; + this.sliderImageWidth = sliderImageWidth; + this.sliderImageHeight = sliderImageHeight; + this.randomX = randomX; + } + + public static ImageCaptchaInfo of(String backgroundImage, + String sliderImage, + Integer bgImageWidth, + Integer bgImageHeight, + Integer sliderImageWidth, + Integer sliderImageHeight, + Integer randomKey) { + return new ImageCaptchaInfo( backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey); + } + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java new file mode 100644 index 0000000..a2ef472 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java @@ -0,0 +1,43 @@ +package cloud.tianai.captcha.template.slider.generator.common.model.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * @Author: 天爱有情 + * @date 2022/4/22 15:49 + * @Description 旋转图片 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RotateImageCaptchaInfo extends ImageCaptchaInfo { + /** + * 旋转多少度 + */ + private Double degree; + + + + public static RotateImageCaptchaInfo of(Double degree, + Integer randomX, + String backgroundImage, + String sliderImage, + Integer bgImageWidth, + Integer bgImageHeight, + Integer sliderImageWidth, + Integer sliderImageHeight) { + RotateImageCaptchaInfo rotateImageCaptchaInfo = new RotateImageCaptchaInfo(); + rotateImageCaptchaInfo.setDegree(degree); + rotateImageCaptchaInfo.setRandomX(randomX); + rotateImageCaptchaInfo.setBackgroundImage(backgroundImage); + rotateImageCaptchaInfo.setSliderImage(sliderImage); + rotateImageCaptchaInfo.setBgImageWidth(bgImageWidth); + rotateImageCaptchaInfo.setBgImageHeight(bgImageHeight); + rotateImageCaptchaInfo.setSliderImageWidth(sliderImageWidth); + rotateImageCaptchaInfo.setSliderImageHeight(sliderImageHeight); + return rotateImageCaptchaInfo; + } + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderCaptchaInfo.java deleted file mode 100644 index 639a232..0000000 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderCaptchaInfo.java +++ /dev/null @@ -1,66 +0,0 @@ -package cloud.tianai.captcha.template.slider.generator.common.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; - -/** - * @Author: 天爱有情 - * @Date 2020/5/29 8:04 - * @Description 滑块验证码 - */ -@Data -@AllArgsConstructor -public class SliderCaptchaInfo { - - /** - * x轴 - */ - private Integer x; - /** - * y轴 - */ - private Integer y; - /** - * 背景图 - */ - private String backgroundImage; - /** - * 移动图 - */ - private String sliderImage; - /** 背景图片宽度. */ - private Integer bgImageWidth; - /** 背景图片高度. */ - private Integer bgImageHeight; - /** 滑块图片宽度. */ - private Integer sliderImageWidth; - /** 滑块图片高度. */ - private Integer sliderImageHeight; - /** - * 扩展字段 - */ - public Object expand; - - public SliderCaptchaInfo(Integer x, Integer y, String backgroundImage, String sliderImage, Integer bgImageWidth, Integer bgImageHeight, Integer sliderImageWidth, Integer sliderImageHeight) { - this.x = x; - this.y = y; - this.backgroundImage = backgroundImage; - this.sliderImage = sliderImage; - this.bgImageWidth = bgImageWidth; - this.bgImageHeight = bgImageHeight; - this.sliderImageWidth = sliderImageWidth; - this.sliderImageHeight = sliderImageHeight; - } - - public static SliderCaptchaInfo of(Integer x, - Integer y, - String backgroundImage, - String sliderImage, - Integer bgImageWidth, - Integer bgImageHeight, - Integer sliderImageWidth, - Integer sliderImageHeight) { - return new SliderCaptchaInfo(x, y, backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight); - } - -} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java new file mode 100644 index 0000000..563cffd --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java @@ -0,0 +1,43 @@ +package cloud.tianai.captcha.template.slider.generator.common.model.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SliderImageCaptchaInfo extends ImageCaptchaInfo { + /** + * x轴 + */ + private Integer x; + /** + * y轴 + */ + private Integer y; + + + public static SliderImageCaptchaInfo of(Integer x, + Integer y, + String backgroundImage, + String sliderImage, + Integer bgImageWidth, + Integer bgImageHeight, + Integer sliderImageWidth, + Integer sliderImageHeight) { + SliderImageCaptchaInfo sliderImageCaptchaInfo = new SliderImageCaptchaInfo(); + sliderImageCaptchaInfo.setX(x); + sliderImageCaptchaInfo.setY(y); + sliderImageCaptchaInfo.setRandomX(x); + sliderImageCaptchaInfo.setBackgroundImage(backgroundImage); + sliderImageCaptchaInfo.setSliderImage(sliderImage); + sliderImageCaptchaInfo.setBgImageWidth(bgImageWidth); + sliderImageCaptchaInfo.setBgImageHeight(bgImageHeight); + sliderImageCaptchaInfo.setSliderImageWidth(sliderImageWidth); + sliderImageCaptchaInfo.setSliderImageHeight(sliderImageHeight); + + return sliderImageCaptchaInfo; + } + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheSliderCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java similarity index 74% rename from src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheSliderCaptchaGenerator.java rename to src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java index 787ea01..9651a27 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheSliderCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java @@ -1,8 +1,8 @@ package cloud.tianai.captcha.template.slider.generator.impl; +import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; -import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator; -import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.common.util.NamedThreadFactory; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; import lombok.Getter; @@ -22,12 +22,12 @@ import java.util.concurrent.atomic.AtomicInteger; * @Description 滑块验证码缓冲器 */ @Slf4j -public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator { +public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator { protected final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("slider-captcha-queue")); - protected ConcurrentLinkedQueue queue; + protected ConcurrentLinkedQueue queue; protected AtomicInteger pos = new AtomicInteger(0); - protected SliderCaptchaGenerator target; + protected ImageCaptchaGenerator target; protected int size; /** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */ protected int waitTime = 1000; @@ -39,13 +39,13 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator { @Setter protected boolean requiredGetCaptcha = true; - public CacheSliderCaptchaGenerator(SliderCaptchaGenerator target, GenerateParam generateParam, int size) { + public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size) { this.target = target; this.generateParam = generateParam; this.size = size; } - public CacheSliderCaptchaGenerator(SliderCaptchaGenerator target, GenerateParam generateParam, int size, int waitTime, int period) { + public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size, int waitTime, int period) { this.target = target; this.generateParam = generateParam; this.size = size; @@ -71,7 +71,7 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator { if (pos.get() >= size) { return; } - SliderCaptchaInfo slideImageInfo = target.generateSlideImageInfo(generateParam); + ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(generateParam); if (slideImageInfo != null) { boolean addStatus = queue.offer(slideImageInfo); if (addStatus) { @@ -101,17 +101,17 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator { @SneakyThrows @Override - public SliderCaptchaInfo generateSlideImageInfo() { - return generateSlideImageInfo(this.requiredGetCaptcha); + public ImageCaptchaInfo generateCaptchaImage(String type) { + return generateCaptchaImage(this.requiredGetCaptcha); } @SneakyThrows - public SliderCaptchaInfo generateSlideImageInfo(boolean requiredGetCaptcha) { - SliderCaptchaInfo poll = queue.poll(); + public ImageCaptchaInfo generateCaptchaImage(boolean requiredGetCaptcha) { + ImageCaptchaInfo poll = queue.poll(); if (poll == null && requiredGetCaptcha) { log.warn("滑块验证码缓存不足, genParam:{}", generateParam); // 如果池内没数据, 则直接生成 - return target.generateSlideImageInfo(generateParam); + return target.generateCaptchaImage(generateParam); } // 减1 pos.decrementAndGet(); @@ -119,18 +119,13 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator { } @Override - public SliderCaptchaInfo generateSlideImageInfo(String targetFormatName, String matrixFormatName) { - return target.generateSlideImageInfo(targetFormatName, matrixFormatName); + public ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName) { + return target.generateCaptchaImage(type,targetFormatName, matrixFormatName); } @Override - public SliderCaptchaInfo generateSlideImageInfo(GenerateParam param) { - return target.generateSlideImageInfo(param); - } - - @Override - public boolean percentageContrast(Float newPercentage, Float oriPercentage) { - return target.percentageContrast(newPercentage, oriPercentage); + public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { + return target.generateCaptchaImage(param); } @Override diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardSliderCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardImageCaptchaGenerator.java similarity index 64% rename from src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardSliderCaptchaGenerator.java rename to src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardImageCaptchaGenerator.java index ee0540d..547633e 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardSliderCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardImageCaptchaGenerator.java @@ -1,24 +1,25 @@ package cloud.tianai.captcha.template.slider.generator.impl; -import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; +import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator; +import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant; -import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator; -import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo; -import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider; -import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderImageCaptchaInfo; import cloud.tianai.captcha.template.slider.resource.ResourceStore; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; -import lombok.Getter; -import lombok.Setter; +import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; +import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import javax.imageio.ImageIO; import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.*; @@ -29,7 +30,7 @@ import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils * @Description 滑块验证码模板 */ @Slf4j -public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator { +public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator { /** * 默认的resource资源文件路径. @@ -40,59 +41,28 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator { */ public static final String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template"; - public static String DEFAULT_BG_IMAGE_TYPE = "jpeg"; - public static String DEFAULT_SLIDER_IMAGE_TYPE = "png"; - public static float DEFAULT_TOLERANT = 0.02f; - protected final SliderCaptchaResourceManager sliderCaptchaResourceManager; - @Getter - @Setter - /** 容错值. */ - public float tolerant = DEFAULT_TOLERANT; - @Getter - @Setter - /** 默认背景图片类型. */ - public String defaultBgImageType = DEFAULT_BG_IMAGE_TYPE; - @Getter - @Setter - /** 默认滑块图片类型. */ - public String defaultSliderImageType = DEFAULT_SLIDER_IMAGE_TYPE; - public StandardSliderCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager, - boolean initDefaultResource) { + public StandardImageCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager, + boolean initDefaultResource) { this.sliderCaptchaResourceManager = sliderCaptchaResourceManager; if (initDefaultResource) { initDefaultResource(); } } - @Override - public SliderCaptchaInfo generateSlideImageInfo() { - return generateSlideImageInfo(defaultBgImageType, defaultSliderImageType); - } - @SneakyThrows @Override - public SliderCaptchaInfo generateSlideImageInfo(String backgroundFormatName, String sliderFormatName) { - return generateSlideImageInfo(GenerateParam.builder() - .backgroundFormatName(backgroundFormatName) - .sliderFormatName(sliderFormatName) - .obfuscate(false) - .build()); - } - - @SneakyThrows - @Override - public SliderCaptchaInfo generateSlideImageInfo(GenerateParam param) { + public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { Boolean obfuscate = param.getObfuscate(); - Map templateImages = sliderCaptchaResourceManager.randomGetTemplate(); + Map templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType()); if (templateImages == null || templateImages.isEmpty()) { return null; } Collection inputStreams = new LinkedList<>(); try { - Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(); + Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(param.getType()); InputStream resourceInputStream = sliderCaptchaResourceManager.getResourceInputStream(resourceImage); inputStreams.add(resourceInputStream); BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream); @@ -151,16 +121,16 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator { * @return SliderCaptchaInfo */ @SneakyThrows - public SliderCaptchaInfo wrapSliderCaptchaInfo(int randomX, - int randomY, - BufferedImage backgroundImage, - BufferedImage sliderImage, - GenerateParam param) { + public SliderImageCaptchaInfo wrapSliderCaptchaInfo(int randomX, + int randomY, + BufferedImage backgroundImage, + BufferedImage sliderImage, + GenerateParam param) { String backgroundFormatName = param.getBackgroundFormatName(); String sliderFormatName = param.getSliderFormatName(); String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName); String sliderImageBase64 = transform(sliderImage, sliderFormatName); - return SliderCaptchaInfo.of(randomX, randomY, + return SliderImageCaptchaInfo.of(randomX, randomY, backGroundImageBase64, sliderImageBase64, backgroundImage.getWidth(), backgroundImage.getHeight(), @@ -168,41 +138,6 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator { ); } - /** - * 将图片转换成字符串格式 - * - * @param bufferedImage 图片 - * @param formatType 格式化类型 - * @return String - */ - public String transform(BufferedImage bufferedImage, String formatType) throws IOException { - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - ImageIO.write(bufferedImage, formatType, byteArrayOutputStream); - //转换成字节码 - byte[] data = byteArrayOutputStream.toByteArray(); - String base64 = Base64.getEncoder().encodeToString(data); - return "data:image/" + formatType + ";base64,".concat(base64); - } - - /** - * 百分比对比 - * - * @param newPercentage 用户百分比 - * @param oriPercentage 原百分比 - * @return true 成功 false 失败 - */ - @Override - public boolean percentageContrast(Float newPercentage, Float oriPercentage) { - if (newPercentage == null || Float.isNaN(newPercentage) || Float.isInfinite(newPercentage) - || oriPercentage == null || Float.isNaN(oriPercentage) || Float.isInfinite(oriPercentage)) { - return false; - } - // 容错值 - float maxTolerant = oriPercentage + tolerant; - float minTolerant = oriPercentage - tolerant; - return newPercentage >= minTolerant && newPercentage <= maxTolerant; - } - @Override public SliderCaptchaResourceManager getSlideImageResourceManager() { return sliderCaptchaResourceManager; @@ -217,34 +152,26 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator { return ThreadLocalRandom.current().nextInt(slWidth, sliderX - slWidth); } - protected InputStream getTemplateFile(Map templateImages, String imageName) { - Resource resource = templateImages.get(imageName); - if (resource == null) { - throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName)); - } - return sliderCaptchaResourceManager.getResourceInputStream(resource); - } - /** * 初始化默认资源 */ public void initDefaultResource() { ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); // 添加一些系统的资源文件 - resourceStore.addResource(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"))); // 添加一些系统的 模板文件 Map template1 = new HashMap<>(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"))); - resourceStore.addTemplate(template1); + resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template1); Map template2 = new HashMap<>(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"))); - resourceStore.addTemplate(template2); + resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template2); } } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateCaptchaGenerator.java new file mode 100644 index 0000000..12654b3 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateCaptchaGenerator.java @@ -0,0 +1,147 @@ +package cloud.tianai.captcha.template.slider.generator.impl; + +import cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils; +import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator; +import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant; +import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.RotateImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.resource.ResourceStore; +import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; +import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; +import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider; +import lombok.SneakyThrows; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.FileOutputStream; +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.concurrent.ThreadLocalRandom; + +import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.*; +import static cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH; +import static cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH; + +/** + * @Author: 天爱有情 + * @date 2022/4/22 16:43 + * @Description 旋转图片验证码生成器 + */ +public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerator { + + protected final SliderCaptchaResourceManager sliderCaptchaResourceManager; + + public StandardRotateCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager, boolean initDefaultResource) { + this.sliderCaptchaResourceManager = sliderCaptchaResourceManager; + if (initDefaultResource) { + initDefaultResource(); + } + } + + public void initDefaultResource() { + ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); + // 添加一些系统的资源文件 + resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); + + // 添加一些系统的 模板文件 + Map template1 = new HashMap<>(4); + template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png"))); + template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png"))); + resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1); + } + + @Override + public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { + // 旋转验证码没有混淆 + Boolean obfuscate = param.getObfuscate(); + Map templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType()); + if (templateImages == null || templateImages.isEmpty()) { + return null; + } + Collection inputStreams = new LinkedList<>(); + try { + Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(param.getType()); + InputStream resourceInputStream = sliderCaptchaResourceManager.getResourceInputStream(resourceImage); + inputStreams.add(resourceInputStream); + BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream); + // 拷贝一份图片 + BufferedImage targetBackground = deepCopyBufferedImage(cutBackground); + + InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME); + inputStreams.add(fixedTemplateInput); + BufferedImage fixedTemplate = wrapFile2BufferedImage(fixedTemplateInput); + + InputStream activeTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME); + inputStreams.add(activeTemplateInput); + BufferedImage activeTemplate = wrapFile2BufferedImage(activeTemplateInput); + // 算出居中的x和y + int x = targetBackground.getWidth() / 2 - fixedTemplate.getWidth() / 2; + int y = targetBackground.getHeight() / 2 - fixedTemplate.getHeight() / 2; + overlayImage(targetBackground, fixedTemplate, x, y); + // 抠图部分 + BufferedImage cutImage = cutImage(cutBackground, fixedTemplate, x, y); + overlayImage(cutImage, activeTemplate, 0, 0); + // 随机旋转抠图部分 + // 随机x, 转换为角度 + int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 10, targetBackground.getWidth() - 10); + double degree = randomX / ((targetBackground.getWidth()) / 360d); +// int degree = ThreadLocalRandom.current().nextInt(10, 350); + cutImage = rotateImage(cutImage, degree); + return wrapRotateCaptchaInfo(degree, randomX, targetBackground, cutImage, param); + } finally { + // 使用完后关闭流 + for (InputStream inputStream : inputStreams) { + try { + inputStream.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + @SneakyThrows + private ImageCaptchaInfo wrapRotateCaptchaInfo(double degree, int randomX, BufferedImage backgroundImage, BufferedImage sliderImage, GenerateParam param) { + String backgroundFormatName = param.getBackgroundFormatName(); + String sliderFormatName = param.getSliderFormatName(); + String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName); + String sliderImageBase64 = transform(sliderImage, sliderFormatName); + return RotateImageCaptchaInfo.of(degree, + randomX, + backGroundImageBase64, + sliderImageBase64, + backgroundImage.getWidth(), backgroundImage.getHeight(), + sliderImage.getWidth(), sliderImage.getHeight() + ); + } + + @Override + public SliderCaptchaResourceManager getSlideImageResourceManager() { + return sliderCaptchaResourceManager; + } + + public static void main(String[] args) throws IOException { + BufferedImage bgImage = CaptchaImageUtils.wrapFile2BufferedImage( + new FileInputStream("E:\\projects\\tianai-captcha\\src\\main\\resources\\META-INF\\cut-image\\resource\\1.jpg")); + BufferedImage image1 = CaptchaImageUtils.wrapFile2BufferedImage(new FileInputStream("C:\\Users\\Thinkpad\\Desktop\\a.png")); + BufferedImage image2 = CaptchaImageUtils.wrapFile2BufferedImage(new FileInputStream("C:\\Users\\Thinkpad\\Desktop\\b.png")); + + BufferedImage cutImage = CaptchaImageUtils.cutImage(bgImage, image1, bgImage.getWidth() / 2 - 100, bgImage.getHeight() / 2 - 100); + CaptchaImageUtils.overlayImage(cutImage, image2, 0, 0); + cutImage = CaptchaImageUtils.rotateImage(cutImage, 180); + CaptchaImageUtils.overlayImage(bgImage, image1, bgImage.getWidth() / 2 - 100, bgImage.getHeight() / 2 - 100); + FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\a1.jpg"); + FileOutputStream file2OutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\a2.png"); + ImageIO.write(bgImage, "jpeg", fileOutputStream); + ImageIO.write(cutImage, "png", file2OutputStream); + fileOutputStream.close(); + file2OutputStream.close(); + } +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/resource/ResourceStore.java b/src/main/java/cloud/tianai/captcha/template/slider/resource/ResourceStore.java index ad7918d..2f03b3e 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/resource/ResourceStore.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/resource/ResourceStore.java @@ -12,97 +12,97 @@ public interface ResourceStore { * * @param resource 资源 */ - void addResource(Resource resource); + void addResource(String type, Resource resource); /** - * 设置资源 + * 清除某个类型下的所有资源 * - * @param resources resources + * @param type type */ - void setResource(List resources); - - /** - * 删除资源 - * - * @param resource resource - */ - boolean deleteResource(Resource resource); + void clearResources(String type); /** * 清除所有资源 */ - void clearResources(); - - /** - * 添加模板 - * - * @param template template - */ - void addTemplate(Map template); - - - /** - * 设置模板 - * - * @param templateResource templateResource - */ - void setTemplates(List> templateResource); - - /** - * 删除模板 - * - * @param template template - */ - void deleteTemplate(Map template); - - /** - * 清除所有模板 - */ - void clearTemplates(); - + void clearAllResources(); /** * 获取所有资源对象 * * @return List */ - List listResources(); + Map> listAllResources(); /** - * 获取所有模板 + * 获取某个type下的所有资源对象 * - * @return List> + * @param type type + * @return List */ - List> listTemplates(); + List listResourcesByType(String type); + + /** + * 随机获取某个资源 + * + * @param type type + * @return Resource + */ + Resource randomGetResource(String type); /** * 获取资源总数 * * @return int */ - int getResourceCount(); + int getAllResourceCount(); /** - * 获取模板count + * 获取某个type下的资源总数 * + * @param type type * @return int */ - int getTemplateCount(); + int getResourceCount(String type); /** - * 获取资源通过index + * 添加模板 * - * @param index index - * @return Resource + * @param template template */ - Resource getResourceByIndex(int index); + void addTemplate(String type, Map template); /** - * 获取模板通过indx + * 清除所有模板 + */ + void clearAllTemplates(); + + /** + * 清除某个type下的所有模板 * - * @param index index + * @param type type + */ + void clearTemplates(String type); + + /** + * 获取所有模板通过type + * + * @return List> + */ + List> listTemplatesByType(String type); + + /** + * 获取所有模板 + * + * @return Map>> + */ + Map>> listAllTemplates(); + + /** + * 随机获取某个模板通过type + * + * @param type type * @return Map */ - Map getTemplateByIndex(int index); + Map randomGetTemplateByType(String type); } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java index 22eace4..2cc6fee 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java @@ -18,14 +18,14 @@ public interface SliderCaptchaResourceManager { * * @return Map */ - Map randomGetTemplate(); + Map randomGetTemplate(String type); /** * 随机获取某个资源对象 * * @return Resource */ - Resource randomGetResource(); + Resource randomGetResource(String type); /** * 获取真正的资源流通过资源对象 diff --git a/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultResourceStore.java b/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultResourceStore.java index 2f8eae1..1cc7eeb 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultResourceStore.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultResourceStore.java @@ -1,12 +1,11 @@ package cloud.tianai.captcha.template.slider.resource.impl; +import cloud.tianai.captcha.template.slider.common.util.CollectionUtils; import cloud.tianai.captcha.template.slider.resource.ResourceStore; import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; /** * @Author: 天爱有情 @@ -17,86 +16,103 @@ public class DefaultResourceStore implements ResourceStore { /** * 模板资源. */ - private List> templateResourceList = new ArrayList<>(2); + private Map>> templateResourceMap = new HashMap<>(2); /** * resource. */ - private List resourceList = new ArrayList<>(20); + private Map> resourceMap = new HashMap<>(2); @Override - public void addResource(Resource resource) { - resourceList.add(resource); + public void addResource(String type, Resource resource) { + resourceMap.computeIfAbsent(type, k -> new ArrayList<>(20)).add(resource); } @Override - public void setResource(List resources) { - resourceList = new ArrayList<>(resources); + public void clearResources(String type) { + resourceMap.remove(type); } @Override - public boolean deleteResource(Resource resource) { - return resourceList.remove(resource); + public void clearAllResources() { + resourceMap.clear(); } @Override - public void clearResources() { - resourceList.clear(); + public Map> listAllResources() { + return resourceMap; } @Override - public void addTemplate(Map template) { - templateResourceList.add(template); + public List listResourcesByType(String type) { + return resourceMap.getOrDefault(type, Collections.emptyList()); } @Override - public void setTemplates(List> templateResource) { - templateResourceList = new ArrayList<>(templateResource); - } - - @Override - public void deleteTemplate(Map template) { - templateResourceList.remove(template); - } - - @Override - public void clearTemplates() { - templateResourceList.clear(); - } - - @Override - public List listResources() { - return Collections.unmodifiableList(resourceList); - } - - @Override - public List> listTemplates() { - return Collections.unmodifiableList(templateResourceList); - } - - @Override - public int getResourceCount() { - return resourceList.size(); - } - - @Override - public int getTemplateCount() { - return templateResourceList.size(); - } - - @Override - public Resource getResourceByIndex(int index) { - if (index < 0 || index > resourceList.size() - 1) { - throw new IllegalArgumentException("错误的index"); + public Resource randomGetResource(String type) { + List resources = resourceMap.get(type); + if (CollectionUtils.isEmpty(resources)) { + throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type); } - return resourceList.get(index); + if (resources.size() == 1) { + return resources.get(0); + } + int randomIndex = ThreadLocalRandom.current().nextInt(resources.size()); + return resources.get(randomIndex); } @Override - public Map getTemplateByIndex(int index) { - if (index < 0 || index > templateResourceList.size() - 1) { - throw new IllegalArgumentException("错误的index"); + public int getAllResourceCount() { + int count = 0; + for (List value : resourceMap.values()) { + count += value.size(); } - return templateResourceList.get(index); + return count; } + + @Override + public int getResourceCount(String type) { + return resourceMap.getOrDefault(type, Collections.emptyList()).size(); + } + + + @Override + public void addTemplate(String type, Map template) { + templateResourceMap.computeIfAbsent(type, k -> new ArrayList<>(2)).add(template); + } + + @Override + public void clearAllTemplates() { + templateResourceMap.clear(); + } + + @Override + public void clearTemplates(String type) { + templateResourceMap.remove(type); + } + + @Override + public List> listTemplatesByType(String type) { + return templateResourceMap.getOrDefault(type, Collections.emptyList()); + } + + @Override + public Map>> listAllTemplates() { + return templateResourceMap; + } + + @Override + public Map randomGetTemplateByType(String type) { + List> templateList = templateResourceMap.get(type); + if (CollectionUtils.isEmpty(templateList)) { + throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type); + } + + if (templateList.size() == 1) { + return templateList.get(0); + } + int randomIndex = ThreadLocalRandom.current().nextInt(templateList.size()); + return templateList.get(randomIndex); + } + } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultSliderCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultSliderCaptchaResourceManager.java index 234c002..1e213a1 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultSliderCaptchaResourceManager.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultSliderCaptchaResourceManager.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; /** * @Author: 天爱有情 @@ -47,29 +46,21 @@ public class DefaultSliderCaptchaResourceManager implements SliderCaptchaResourc } @Override - public Map randomGetTemplate() { - int count = resourceStore.getTemplateCount(); - if (count < 1) { - throw new IllegalStateException("随机获取模板错误,store中模板为空"); + public Map randomGetTemplate(String type) { + Map resourceMap = resourceStore.randomGetTemplateByType(type); + if (resourceMap == null) { + throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type); } - if (count == 1) { - return resourceStore.getTemplateByIndex(0); - } - int randomIndex = ThreadLocalRandom.current().nextInt(count); - return resourceStore.getTemplateByIndex(randomIndex); + return resourceMap; } @Override - public Resource randomGetResource() { - int count = resourceStore.getResourceCount(); - if (count < 1) { - throw new IllegalStateException("随机获取资源错误,store中资源为空"); + public Resource randomGetResource(String type) { + Resource resource = resourceStore.randomGetResource(type); + if (resource == null) { + throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type); } - if (count == 1) { - return resourceStore.getResourceByIndex(0); - } - int randomIndex = ThreadLocalRandom.current().nextInt(count); - return resourceStore.getResourceByIndex(randomIndex); + return resource; } @Override diff --git a/src/main/java/cloud/tianai/captcha/template/slider/validator/SliderCaptchaValidator.java b/src/main/java/cloud/tianai/captcha/template/slider/validator/SliderCaptchaValidator.java index 6e40446..76e1e8e 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/validator/SliderCaptchaValidator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/validator/SliderCaptchaValidator.java @@ -1,6 +1,6 @@ package cloud.tianai.captcha.template.slider.validator; -import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack; import java.util.Map; @@ -43,10 +43,10 @@ public interface SliderCaptchaValidator { /** * 用于生成验证码校验时需要的回传参数 * - * @param sliderCaptchaInfo 生成的验证码数据 + * @param imageCaptchaInfo 生成的验证码数据 * @return Map */ - Map generateSliderCaptchaValidData(SliderCaptchaInfo sliderCaptchaInfo); + Map generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo); /** * 校验用户滑动滑块是否正确 diff --git a/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleSliderCaptchaValidator.java b/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleSliderCaptchaValidator.java index c7bf66d..21f502b 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleSliderCaptchaValidator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleSliderCaptchaValidator.java @@ -1,7 +1,7 @@ package cloud.tianai.captcha.template.slider.validator.impl; -import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo; import cloud.tianai.captcha.template.slider.common.util.CollectionUtils; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator; import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack; import lombok.Getter; @@ -56,9 +56,9 @@ public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator { } @Override - public Map generateSliderCaptchaValidData(SliderCaptchaInfo sliderCaptchaInfo) { + public Map generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) { Map map = new HashMap<>(8); - addPercentage(sliderCaptchaInfo, map); + addPercentage(imageCaptchaInfo, map); return map; } @@ -106,8 +106,8 @@ public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator { return null; } - protected void addPercentage(SliderCaptchaInfo sliderCaptchaInfo, Map sliderCaptchaValidData) { - float percentage = calcPercentage(sliderCaptchaInfo.getX(), sliderCaptchaInfo.getBgImageWidth()); + protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map sliderCaptchaValidData) { + float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth()); sliderCaptchaValidData.put("percentage", percentage); } } diff --git a/src/main/resources/META-INF/cut-image/template/3/active.png b/src/main/resources/META-INF/cut-image/template/3/active.png new file mode 100644 index 0000000..924d570 Binary files /dev/null and b/src/main/resources/META-INF/cut-image/template/3/active.png differ diff --git a/src/main/resources/META-INF/cut-image/template/3/fixed.png b/src/main/resources/META-INF/cut-image/template/3/fixed.png new file mode 100644 index 0000000..4295ba3 Binary files /dev/null and b/src/main/resources/META-INF/cut-image/template/3/fixed.png differ diff --git a/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java b/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java new file mode 100644 index 0000000..0a67873 --- /dev/null +++ b/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java @@ -0,0 +1,37 @@ +package example; + +import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant; +import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.RotateImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.generator.impl.StandardRotateCaptchaGenerator; +import cloud.tianai.captcha.template.slider.resource.ResourceStore; +import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; +import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; +import cloud.tianai.captcha.template.slider.resource.impl.DefaultResourceStore; +import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaResourceManager; + +import java.util.HashMap; +import java.util.Map; + +public class StandardRotateCaptchaGeneratorTest { + + public static void main(String[] args) { + ResourceStore resourceStore = new DefaultResourceStore(); +// Map template = new HashMap<>(); +// template.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\a.png")); +// template.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\b.png")); + +// resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template); +// resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource("file", "E:\\projects\\tianai-captcha\\src\\main\\resources\\META-INF\\cut-image\\resource\\1.jpg")); + + SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(resourceStore); + + StandardRotateCaptchaGenerator standardRotateCaptchaGenerator = new StandardRotateCaptchaGenerator(sliderCaptchaResourceManager, true); + ImageCaptchaInfo imageCaptchaInfo = standardRotateCaptchaGenerator.generateCaptchaImage(CaptchaTypeConstant.ROTATE); + System.out.println("backgroundImage:" + imageCaptchaInfo.getBackgroundImage()); + System.out.println("sliderImage:" + imageCaptchaInfo.getSliderImage()); + System.out.println(((RotateImageCaptchaInfo)imageCaptchaInfo).getDegree()); + + } +}