diff --git a/pom.xml b/pom.xml index f2ef225..ef46c0a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 cloud.tianai.captcha tianai-captcha - 1.3.0.RELEASE + 1.3.1.alpha tianai-captcha 滑块验证码 diff --git a/readme.md b/readme.md index 1be5526..329fdeb 100644 --- a/readme.md +++ b/readme.md @@ -34,15 +34,15 @@ ### 2. 使用 `SliderCaptchaGenerator`生成器生成滑块验证码 ```java -import cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator; +import cloud.tianai.captcha.template.slider.generator.impl.StandardSliderImageCaptchaGenerator; 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; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; +import cloud.tianai.captcha.template.slider.resource.impl.DefaultImageCaptchaResourceManager; public class Test { public static void main(String[] args) throws InterruptedException { - SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); - StandardImageCaptchaGenerator sliderCaptchaGenerator = new StandardImageCaptchaGenerator(sliderCaptchaResourceManager, true); + ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager(); + StandardSliderImageCaptchaGenerator sliderCaptchaGenerator = new StandardSliderImageCaptchaGenerator(imageCaptchaResourceManager, true); // 生成滑块图片 SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateCaptchaImage(); System.out.println(slideImageInfo); @@ -101,9 +101,9 @@ public class Test2 { public class Test { public static void main(String[] args) { // 资源管理器 - SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + SliderCaptchaResourceManager imageCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); // 标准验证码生成器 - StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, true); + StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(imageCaptchaResourceManager, true); // 生成滑块图片 SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateSlideImageInfo(GenerateParam.builder() .sliderFormatName("jpeg") @@ -121,9 +121,9 @@ public class Test { public class Test { public static void main(String[] args) { // 资源管理器 - SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + SliderCaptchaResourceManager imageCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); // 标准验证码生成器 - StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, true); + StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(imageCaptchaResourceManager, true); // 生成滑块图片 SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateSlideImageInfo(GenerateParam.builder() .sliderFormatName("webp") @@ -143,7 +143,7 @@ public class Test { public class Test { public static void main(String[] args) { // 通过资源管理器或者资源存储器 - ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); + ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); // 添加classpath目录下的 aa.jpg 图片 resourceStore.addResource(new Resource(ClassPathResourceProvider.NAME, "/aa.jpg")); // 添加远程url图片资源 @@ -165,7 +165,7 @@ public class Test { public class Test { public static void main(String[] args) { // 通过资源管理器或者资源存储器 - ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); + ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); // 添加模板.模板图片由三张图片组成 Map template1 = new HashMap<>(4); template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/active.png")); @@ -185,7 +185,7 @@ public class Test { public static void main(String[] args) { //为方便快速上手 系统本身自带了一张图片和两套滑块模板,如果不想用系统自带的可以不让它加载系统自带的 // 第二个构造参数设置为false时将不加载默认的图片和模板 - SliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, false); + SliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(imageCaptchaResourceManager, false); } } ``` @@ -207,10 +207,10 @@ public class Test { public class Test { public static void main(String[] args) { // 实现了 ResourceProvider 后 - SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); - StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, true); + SliderCaptchaResourceManager imageCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(imageCaptchaResourceManager, true); // 注册 - sliderCaptchaResourceManager.registerResourceProvider(new CustomResourceProvider()); + imageCaptchaResourceManager.registerResourceProvider(new CustomResourceProvider()); } } ``` @@ -227,8 +227,8 @@ public class Test { // 参数二: 默认提前缓存多少个 // 参数三: 出错后 等待xx时间再进行生成 // 参数四: 检查时间间隔 - SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); - SliderCaptchaGenerator sliderCaptchaGenerator = new CacheSliderCaptchaGenerator(new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, true), 10, 1000, 100); + SliderCaptchaResourceManager imageCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + SliderCaptchaGenerator sliderCaptchaGenerator = new CacheSliderCaptchaGenerator(new StandardSliderCaptchaGenerator(imageCaptchaResourceManager, true), 10, 1000, 100); // 生成滑块图片 SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateSlideImageInfo(); // 获取背景图片的base64 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 dc3cc95..7b91d32 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 @@ -215,4 +215,14 @@ public class CaptchaImageUtils { return img; } + public static void centerOverlayAndRotateImage(BufferedImage baseBufferedImage, BufferedImage coverBufferedImage, + final double degree) { + coverBufferedImage = rotateImage(coverBufferedImage, degree); + int bw = baseBufferedImage.getWidth(); + int bh = baseBufferedImage.getHeight(); + int cw = coverBufferedImage.getWidth(); + int ch = coverBufferedImage.getHeight(); + overlayImage(baseBufferedImage, coverBufferedImage, bw / 2 - cw / 2, bh / 2 - ch / 2); + } + } 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 index 9e69a35..26ee06a 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java @@ -14,7 +14,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.Base64; import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; /** * @Author: 天爱有情 @@ -71,7 +70,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener if (resource == null) { throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName)); } - return getSlideImageResourceManager().getResourceInputStream(resource); + return getImageResourceManager().getResourceInputStream(resource); } } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java index d3489f3..2f4411e 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/ImageCaptchaGenerator.java @@ -3,8 +3,7 @@ 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.ImageCaptchaInfo; -import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; -import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; /** * @Author: 天爱有情 @@ -45,6 +44,6 @@ public interface ImageCaptchaGenerator { * * @return SliderCaptchaResourceManager */ - SliderCaptchaResourceManager getSlideImageResourceManager(); + ImageCaptchaResourceManager getImageResourceManager(); } 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 255bac7..023cb9d 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 @@ -1,9 +1,7 @@ package cloud.tianai.captcha.template.slider.generator.common.model.dto; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant; +import lombok.*; /** * @Author: 天爱有情 @@ -14,6 +12,7 @@ import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor +@EqualsAndHashCode public class GenerateParam { /** 背景格式化名称.*/ private String backgroundFormatName = "jpeg"; @@ -22,5 +21,5 @@ public class GenerateParam { /** 是否混淆.*/ private Boolean obfuscate = false; /** 类型.*/ - private String type; + private String type = CaptchaTypeConstant.SLIDER; } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java index 9651a27..b22e8f1 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/CacheImageCaptchaGenerator.java @@ -1,19 +1,17 @@ package cloud.tianai.captcha.template.slider.generator.impl; +import cloud.tianai.captcha.template.slider.common.util.NamedThreadFactory; 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.common.model.dto.ImageCaptchaInfo; -import cloud.tianai.captcha.template.slider.common.util.NamedThreadFactory; -import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; import lombok.Getter; import lombok.Setter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.Map; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** @@ -25,29 +23,26 @@ import java.util.concurrent.atomic.AtomicInteger; public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator { protected final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("slider-captcha-queue")); - protected ConcurrentLinkedQueue queue; - protected AtomicInteger pos = new AtomicInteger(0); + protected Map> queueMap = new ConcurrentHashMap<>(8); + protected Map posMap = new ConcurrentHashMap<>(8); protected ImageCaptchaGenerator target; protected int size; /** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */ protected int waitTime = 1000; /** 调度器检查缓存的间隔时间. */ protected int period = 100; - protected GenerateParam generateParam; @Getter @Setter protected boolean requiredGetCaptcha = true; - public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size) { + public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, int size) { this.target = target; - this.generateParam = generateParam; this.size = size; } - public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size, int waitTime, int period) { + public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, int size, int waitTime, int period) { this.target = target; - this.generateParam = generateParam; this.size = size; this.waitTime = waitTime; this.period = period; @@ -62,34 +57,35 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator { private void init(int z) { this.size = z; - this.pos = new AtomicInteger(0); - queue = new ConcurrentLinkedQueue<>(); // 初始化一个队列扫描 scheduledExecutor.scheduleAtFixedRate(() -> { - try { - while (pos.get() < this.size) { - if (pos.get() >= size) { - return; - } - ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(generateParam); - if (slideImageInfo != null) { - boolean addStatus = queue.offer(slideImageInfo); - if (addStatus) { - // 添加记录 - pos.incrementAndGet(); + queueMap.forEach((k, queue) -> { + try { + AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0)); + while (pos.get() < this.size) { + if (pos.get() >= size) { + return; + } + ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(k); + if (slideImageInfo != null) { + boolean addStatus = queue.offer(slideImageInfo); + if (addStatus) { + // 添加记录 + pos.incrementAndGet(); + } + } else { + sleep(); } - } else { - sleep(); } + } catch (Exception e) { + // cache所有 + log.error("缓存队列扫描时出错, ex", e); + // 休眠 + sleep(); } - } catch (Exception e) { - // cache所有 - log.error("缓存队列扫描时出错, ex", e); - // 休眠 - sleep(); - } + }); + }, 0, period, TimeUnit.MILLISECONDS); - log.info("缓存滑块验证码调度器初始化完成: size:{}, genParam:{}", size, generateParam); } private void sleep() { @@ -102,11 +98,14 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator { @SneakyThrows @Override public ImageCaptchaInfo generateCaptchaImage(String type) { - return generateCaptchaImage(this.requiredGetCaptcha); + GenerateParam generateParam = new GenerateParam(); + generateParam.setType(type); + return generateCaptchaImage(generateParam, this.requiredGetCaptcha); } @SneakyThrows - public ImageCaptchaInfo generateCaptchaImage(boolean requiredGetCaptcha) { + public ImageCaptchaInfo generateCaptchaImage(GenerateParam generateParam, boolean requiredGetCaptcha) { + ConcurrentLinkedQueue queue = queueMap.computeIfAbsent(generateParam, g -> new ConcurrentLinkedQueue<>()); ImageCaptchaInfo poll = queue.poll(); if (poll == null && requiredGetCaptcha) { log.warn("滑块验证码缓存不足, genParam:{}", generateParam); @@ -114,23 +113,32 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator { return target.generateCaptchaImage(generateParam); } // 减1 - pos.decrementAndGet(); + if (poll == null) { + AtomicInteger pos = posMap.get(generateParam); + if (pos != null) { + pos.decrementAndGet(); + } + } return poll; } @Override public ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName) { - return target.generateCaptchaImage(type,targetFormatName, matrixFormatName); + return generateCaptchaImage(GenerateParam.builder() + .type(type) + .backgroundFormatName(targetFormatName) + .sliderFormatName(matrixFormatName) + .build(), true); } @Override public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { - return target.generateCaptchaImage(param); + return generateCaptchaImage(param, true); } @Override - public SliderCaptchaResourceManager getSlideImageResourceManager() { - return target.getSlideImageResourceManager(); + public ImageCaptchaResourceManager getImageResourceManager() { + return target.getImageResourceManager(); } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java new file mode 100644 index 0000000..9faa162 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java @@ -0,0 +1,64 @@ +package cloud.tianai.captcha.template.slider.generator.impl; + +import cloud.tianai.captcha.template.slider.common.util.ObjectUtils; +import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator; +import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator; +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.ImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: 天爱有情 + * @date 2022/4/24 9:27 + * @Description 根据type 匹配对应的验证码生成器 + */ +public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator { + + private Map imageCaptchaGeneratorMap = new HashMap<>(4); + + private ImageCaptchaResourceManager imageCaptchaResourceManager; + private boolean initDefaultResource; + + private String defaultCaptcha = CaptchaTypeConstant.SLIDER; + public MultiImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) { + this.imageCaptchaResourceManager = imageCaptchaResourceManager; + this.initDefaultResource = initDefaultResource; + init(); + } + + protected void init() { + addImageCaptchaGenerator(CaptchaTypeConstant.SLIDER, new StandardSliderImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); + addImageCaptchaGenerator(CaptchaTypeConstant.ROTATE, new StandardRotateImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); + } + + public void addImageCaptchaGenerator(String key, ImageCaptchaGenerator captchaGenerator) { + imageCaptchaGeneratorMap.put(key, captchaGenerator); + } + public ImageCaptchaGenerator removeImageCaptchaGenerator(String key) { + return imageCaptchaGeneratorMap.remove(key); + } + + @Override + public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { + String type = param.getType(); + if (ObjectUtils.isEmpty(type)){ + param.setType(defaultCaptcha); + type = defaultCaptcha; + } + ImageCaptchaGenerator imageCaptchaGenerator = imageCaptchaGeneratorMap.get(type); + if (imageCaptchaGenerator == null) { + throw new IllegalArgumentException("生成验证码失败,错误的type类型:" + type); + } + + return imageCaptchaGenerator.generateCaptchaImage(param); + } + + @Override + public ImageCaptchaResourceManager getImageResourceManager() { + return imageCaptchaResourceManager; + } +} 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/StandardRotateImageCaptchaGenerator.java similarity index 75% rename from src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateCaptchaGenerator.java rename to src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateImageCaptchaGenerator.java index 12654b3..f784e57 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardRotateImageCaptchaGenerator.java @@ -7,8 +7,8 @@ import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCapt 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.ImageCaptchaResourceManager; 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; @@ -26,27 +26,27 @@ 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; +import static cloud.tianai.captcha.template.slider.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH; +import static cloud.tianai.captcha.template.slider.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH; /** * @Author: 天爱有情 * @date 2022/4/22 16:43 * @Description 旋转图片验证码生成器 */ -public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerator { +public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGenerator { - protected final SliderCaptchaResourceManager sliderCaptchaResourceManager; + protected final ImageCaptchaResourceManager imageCaptchaResourceManager; - public StandardRotateCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager, boolean initDefaultResource) { - this.sliderCaptchaResourceManager = sliderCaptchaResourceManager; + public StandardRotateImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) { + this.imageCaptchaResourceManager = imageCaptchaResourceManager; if (initDefaultResource) { initDefaultResource(); } } public void initDefaultResource() { - ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); + ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); // 添加一些系统的资源文件 resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); @@ -54,6 +54,7 @@ public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerato 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"))); + template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/matrix.png"))); resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1); } @@ -61,14 +62,14 @@ public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerato public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { // 旋转验证码没有混淆 Boolean obfuscate = param.getObfuscate(); - Map templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType()); + Map templateImages = imageCaptchaResourceManager.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); + Resource resourceImage = imageCaptchaResourceManager.randomGetResource(param.getType()); + InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage); inputStreams.add(resourceInputStream); BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream); // 拷贝一份图片 @@ -81,6 +82,11 @@ public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerato InputStream activeTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME); inputStreams.add(activeTemplateInput); BufferedImage activeTemplate = wrapFile2BufferedImage(activeTemplateInput); + + InputStream matrixTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME); + inputStreams.add(matrixTemplateInput); + BufferedImage matrixTemplate = wrapFile2BufferedImage(matrixTemplateInput); + // 算出居中的x和y int x = targetBackground.getWidth() / 2 - fixedTemplate.getWidth() / 2; int y = targetBackground.getHeight() / 2 - fixedTemplate.getHeight() / 2; @@ -91,10 +97,10 @@ public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerato // 随机旋转抠图部分 // 随机x, 转换为角度 int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 10, targetBackground.getWidth() - 10); - double degree = randomX / ((targetBackground.getWidth()) / 360d); + double degree = 360d - randomX / ((targetBackground.getWidth()) / 360d); // int degree = ThreadLocalRandom.current().nextInt(10, 350); - cutImage = rotateImage(cutImage, degree); - return wrapRotateCaptchaInfo(degree, randomX, targetBackground, cutImage, param); + centerOverlayAndRotateImage(matrixTemplate, cutImage, degree); + return wrapRotateCaptchaInfo(degree, randomX, targetBackground, matrixTemplate, param); } finally { // 使用完后关闭流 for (InputStream inputStream : inputStreams) { @@ -122,9 +128,19 @@ public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerato ); } +// @Override +// public String transform(BufferedImage bufferedImage, String formatType) throws IOException { +// FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\aa" + formatType + "." + formatType); +// ImageIO.write(bufferedImage, formatType, fileOutputStream); +// fileOutputStream.close(); +//// return super.transform(bufferedImage, formatType); +// return ""; +// } + + @Override - public SliderCaptchaResourceManager getSlideImageResourceManager() { - return sliderCaptchaResourceManager; + public ImageCaptchaResourceManager getImageResourceManager() { + return imageCaptchaResourceManager; } public static void main(String[] args) throws IOException { diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardSliderImageCaptchaGenerator.java similarity index 88% rename from src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardImageCaptchaGenerator.java rename to src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardSliderImageCaptchaGenerator.java index 547633e..1db1c2a 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardSliderImageCaptchaGenerator.java @@ -6,8 +6,8 @@ import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCapt 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.ImageCaptchaResourceManager; 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; @@ -30,7 +30,7 @@ import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils * @Description 滑块验证码模板 */ @Slf4j -public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator { +public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGenerator { /** * 默认的resource资源文件路径. @@ -41,12 +41,12 @@ public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator */ public static final String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template"; - protected final SliderCaptchaResourceManager sliderCaptchaResourceManager; + protected final ImageCaptchaResourceManager imageCaptchaResourceManager; - public StandardImageCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager, - boolean initDefaultResource) { - this.sliderCaptchaResourceManager = sliderCaptchaResourceManager; + public StandardSliderImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, + boolean initDefaultResource) { + this.imageCaptchaResourceManager = imageCaptchaResourceManager; if (initDefaultResource) { initDefaultResource(); } @@ -56,14 +56,14 @@ public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator @Override public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { Boolean obfuscate = param.getObfuscate(); - Map templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType()); + Map templateImages = imageCaptchaResourceManager.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); + Resource resourceImage = imageCaptchaResourceManager.randomGetResource(param.getType()); + InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage); inputStreams.add(resourceInputStream); BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream); // 拷贝一份图片 @@ -139,8 +139,8 @@ public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator } @Override - public SliderCaptchaResourceManager getSlideImageResourceManager() { - return sliderCaptchaResourceManager; + public ImageCaptchaResourceManager getImageResourceManager() { + return imageCaptchaResourceManager; } protected int randomObfuscateX(int sliderX, int slWidth, int bgWidth) { @@ -156,7 +156,7 @@ public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator * 初始化默认资源 */ public void initDefaultResource() { - ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); + ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); // 添加一些系统的资源文件 resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); diff --git a/src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/template/slider/resource/ImageCaptchaResourceManager.java similarity index 93% rename from src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java rename to src/main/java/cloud/tianai/captcha/template/slider/resource/ImageCaptchaResourceManager.java index 2cc6fee..80e8fdf 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/resource/SliderCaptchaResourceManager.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/resource/ImageCaptchaResourceManager.java @@ -9,9 +9,9 @@ import java.util.Map; /** * @Author: 天爱有情 * @date 2021/8/7 15:26 - * @Description 滑块验证码图片资源管理器 + * @Description 验证码图片资源管理器 */ -public interface SliderCaptchaResourceManager { +public interface ImageCaptchaResourceManager { /** * 随机获取某个模板 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/DefaultImageCaptchaResourceManager.java similarity index 92% rename from src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultSliderCaptchaResourceManager.java rename to src/main/java/cloud/tianai/captcha/template/slider/resource/impl/DefaultImageCaptchaResourceManager.java index 1e213a1..511bf1a 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/DefaultImageCaptchaResourceManager.java @@ -1,8 +1,8 @@ package cloud.tianai.captcha.template.slider.resource.impl; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.ResourceProvider; 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 cloud.tianai.captcha.template.slider.resource.impl.provider.FileResourceProvider; @@ -19,18 +19,18 @@ import java.util.Map; * @date 2021/8/7 15:35 * @Description 默认的滑块验证码资源管理 */ -public class DefaultSliderCaptchaResourceManager implements SliderCaptchaResourceManager { +public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceManager { private ResourceStore resourceStore; private List resourceProviderList = new ArrayList<>(8); - public DefaultSliderCaptchaResourceManager() { + public DefaultImageCaptchaResourceManager() { init(); } - public DefaultSliderCaptchaResourceManager(ResourceStore resourceStore) { + public DefaultImageCaptchaResourceManager(ResourceStore resourceStore) { this.resourceStore = resourceStore; init(); } diff --git a/src/main/resources/META-INF/cut-image/template/3/matrix.png b/src/main/resources/META-INF/cut-image/template/3/matrix.png new file mode 100644 index 0000000..f4f18eb Binary files /dev/null and b/src/main/resources/META-INF/cut-image/template/3/matrix.png differ diff --git a/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java b/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java index 0a67873..e882a8f 100644 --- a/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java +++ b/src/main/test/java/example/StandardRotateCaptchaGeneratorTest.java @@ -1,18 +1,13 @@ 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.generator.impl.StandardRotateImageCaptchaGenerator; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; 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.DefaultImageCaptchaResourceManager; 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 { @@ -25,10 +20,10 @@ public class StandardRotateCaptchaGeneratorTest { // 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); + ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager(resourceStore); - StandardRotateCaptchaGenerator standardRotateCaptchaGenerator = new StandardRotateCaptchaGenerator(sliderCaptchaResourceManager, true); - ImageCaptchaInfo imageCaptchaInfo = standardRotateCaptchaGenerator.generateCaptchaImage(CaptchaTypeConstant.ROTATE); + StandardRotateImageCaptchaGenerator standardRotateImageCaptchaGenerator = new StandardRotateImageCaptchaGenerator(imageCaptchaResourceManager, true); + ImageCaptchaInfo imageCaptchaInfo = standardRotateImageCaptchaGenerator.generateCaptchaImage(CaptchaTypeConstant.ROTATE); System.out.println("backgroundImage:" + imageCaptchaInfo.getBackgroundImage()); System.out.println("sliderImage:" + imageCaptchaInfo.getSliderImage()); System.out.println(((RotateImageCaptchaInfo)imageCaptchaInfo).getDegree());