From b56919f17daa08ef39db7729a28da39f4a4f0e1a Mon Sep 17 00:00:00 2001 From: liushaofeng Date: Sat, 7 Aug 2021 17:16:57 +0800 Subject: [PATCH] =?UTF-8?q?U=20=E4=BF=AE=E6=94=B9=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=92=8C=E5=AD=98=E5=82=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=20=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC=E4=B8=BA1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- readme.md | 80 +++++-- .../slider/CacheSliderCaptchaTemplate.java | 85 +++---- .../template/slider/DefaultResourceStore.java | 99 ++++++++ .../DefaultSliderCaptchaResourceManager.java | 106 +++++++++ .../slider/DefaultSliderCaptchaTemplate.java | 211 +++++------------- .../captcha/template/slider/Resource.java | 18 ++ .../template/slider/ResourceProvider.java | 34 +++ .../template/slider/ResourceStore.java | 106 +++++++++ .../slider/SliderCaptchaConstant.java | 16 ++ .../slider/SliderCaptchaResourceManager.java | 71 ++++++ .../slider/SliderCaptchaTemplate.java | 20 +- .../provider/ClassPathResourceProvider.java | 42 ++++ .../slider/provider/URLResourceProvider.java | 35 +++ 14 files changed, 702 insertions(+), 223 deletions(-) create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/DefaultResourceStore.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaResourceManager.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/Resource.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/ResourceProvider.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/ResourceStore.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaConstant.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaResourceManager.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/provider/ClassPathResourceProvider.java create mode 100644 src/main/java/cloud/tianai/captcha/template/slider/provider/URLResourceProvider.java diff --git a/pom.xml b/pom.xml index cfc80c1..3f82268 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 cloud.tianai.captcha tianai-captcha - 1.1 + 1.2 tianai-captcha 滑块验证码 diff --git a/readme.md b/readme.md index d636c2e..f4cd59b 100644 --- a/readme.md +++ b/readme.md @@ -9,19 +9,21 @@ - java获取滑块验证码例子 +## 快速上手 +1. 导入xml ```xml cloud.tianai.captcha tianai-captcha - 1.1 + 1.2 ``` - - +2. 使用 `SliderCaptchaTemplate`获取滑块验证码 ```java -public static void main(String[] args) { - SliderCaptchaTemplate sliderCaptchaTemplate = new SliderCaptchaTemplate(); +public static void main(String[] args) throws InterruptedException { + SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, true); // 生成滑块图片 SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo(); // 获取背景图片的base64 @@ -30,18 +32,66 @@ public static void main(String[] args) { slideImageInfo.getSliderImage(); // 获取滑块被背景图片的百分比, (校验图片使用) Float xPercent = slideImageInfo.getXPercent(); + + System.out.println(slideImageInfo); } ``` -- 添加自定义背景图片例子 +# 常用接口 +- 添加自定义图片资源 ```java -addResource(getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); + ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore();= + // 添加classpath目录下的 aa.jpg 图片 + resourceStore.addResource(new Resource(ClassPathResourceProvider.NAME, "/aa.jpg")); + // 添加远程url图片资源 + resourceStore.addResource(new Resource(URLResourceProvider.NAME, "http://www.xx.com/aa.jpg")); + // 内置了通过url 和 classpath读取图片资源,如果想扩展可实现 ResourceProvider 接口,进行自定义扩展 ``` -- 添加自定义模板(滑块的颜色和形状) +- 添加自定义模板资源 ```java -Map template1 = new HashMap<>(4); -template1.put(ACTIVE_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png"))); -template1.put(CUT_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/cut.png"))); -template1.put(FIXED_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png"))); -template1.put(MATRIX_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/matrix.png"))); -addTemplate(template1); -``` \ No newline at end of file + ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore();= + Map template1 = new HashMap<>(4); + template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME,"/active.png")); + template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/fixed.png")); + template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/matrix.png")); + resourceStore.addTemplate(template1); + + // 模板与三张图片组成 滑块、凹槽、背景图 + // 同样默认支持 classpath 和 url 两种获取图片资源, 如果想扩展可实现 ResourceProvider 接口,进行自定义扩展 +``` +- 清除内置的图片资源和模板资源 + ```java + //为方便快速上手 系统本身自带了一张图片和两套滑块模板,如果不想用系统自带的可以不让它加载系统自带的 + // 第二个构造参数设置为false时将不加载默认的图片和模板 + SliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, false); +``` +- 扩展,对`DefaultSliderCaptchaTemplate`增加了缓存模块 +```java +public static void main(String[] args) throws InterruptedException { + // 使用 CacheSliderCaptchaTemplate 对滑块验证码进行缓存,使其提前生成滑块图片 + // 参数一: 真正实现 滑块的 SliderCaptchaTemplate + // 参数二: 默认提前缓存多少个 + // 参数三: 出错后 等待xx时间再进行生成 + // 参数四: 检查时间间隔 + SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new CacheSliderCaptchaTemplate(new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, true), 10, 1000, 100); + // 生成滑块图片 + SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo(); + // 获取背景图片的base64 + String backgroundImage = slideImageInfo.getBackgroundImage(); + // 获取滑块图片 + slideImageInfo.getSliderImage(); + // 获取滑块被背景图片的百分比, (校验图片使用) + Float xPercent = slideImageInfo.getXPercent(); + + System.out.println(slideImageInfo); +} +``` +- 自定义 `ResourceProvider` 实现自定义文件读取策略, 比如 oss之类的 + +```java + // 实现了 ResourceProvider 后 + SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, true); + // 注册 + sliderCaptchaResourceManager.registerResourceProvider(new CustomResourceProvider()); +``` diff --git a/src/main/java/cloud/tianai/captcha/template/slider/CacheSliderCaptchaTemplate.java b/src/main/java/cloud/tianai/captcha/template/slider/CacheSliderCaptchaTemplate.java index baa82eb..1319c3c 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/CacheSliderCaptchaTemplate.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/CacheSliderCaptchaTemplate.java @@ -3,9 +3,6 @@ package cloud.tianai.captcha.template.slider; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import java.net.URL; -import java.util.List; -import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -25,12 +22,23 @@ public class CacheSliderCaptchaTemplate implements SliderCaptchaTemplate { private AtomicInteger pos = new AtomicInteger(0); private SliderCaptchaTemplate target; private int size; + /** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */ + private int waitTime = 1000; + /** 调度器检查缓存的间隔时间. */ + private int period = 100; public CacheSliderCaptchaTemplate(SliderCaptchaTemplate target, int size) { this.target = target; this.size = size; } + public CacheSliderCaptchaTemplate(SliderCaptchaTemplate target, int size, int waitTime, int period) { + this.target = target; + this.size = size; + this.waitTime = waitTime; + this.period = period; + } + /** * 记的初始化调度器 */ @@ -57,22 +65,26 @@ public class CacheSliderCaptchaTemplate implements SliderCaptchaTemplate { pos.incrementAndGet(); } } else { - // 休眠500毫秒 - try { - TimeUnit.MILLISECONDS.sleep(500); - } catch (InterruptedException ignored) { - } + sleep(); } - } } catch (Exception e) { // cache所有 log.error("缓存队列扫描时出错, ex", e); + // 休眠 + sleep(); } - }, 0, 100, TimeUnit.MILLISECONDS); + }, 0, period, TimeUnit.MILLISECONDS); log.info("缓存滑块验证码调度器初始化完成: size:{}", size); } + private void sleep() { + try { + TimeUnit.MILLISECONDS.sleep(waitTime); + } catch (InterruptedException ignored) { + } + } + @SneakyThrows @Override public SliderCaptchaInfo getSlideImageInfo() { @@ -88,60 +100,21 @@ public class CacheSliderCaptchaTemplate implements SliderCaptchaTemplate { } @Override - public void addResource(URL url) { - target.addResource(url); + public SliderCaptchaInfo getSlideImageInfo(String targetFormatName, String matrixFormatName) { + return target.getSlideImageInfo(targetFormatName, matrixFormatName); } - @Override - public void addTemplate(Map template) { - target.addTemplate(template); - } - - @Override - public void setResource(List resources) { - target.setResource(resources); - } - - @Override - public void setTemplates(List> imageTemplates) { - target.setTemplates(imageTemplates); - } - - @Override - public void deleteResource(URL resource) { - target.deleteResource(resource); - } - - @Override - public List listResources() { - return target.listResources(); - } - - @Override - public void clearResources() { - target.clearResources(); - } - - @Override - public void deleteTemplate(Map template) { - target.deleteTemplate(template); - } - - @Override - public List> listTemplates() { - return target.listTemplates(); - } - - @Override - public void clearTemplates() { - target.clearTemplates(); - } @Override public boolean percentageContrast(Float newPercentage, Float oriPercentage) { return target.percentageContrast(newPercentage, oriPercentage); } + @Override + public SliderCaptchaResourceManager getSlideImageResourceManager() { + return target.getSlideImageResourceManager(); + } + // public static void main(String[] args) throws InterruptedException { // SliderCaptchaTemplate captchaTemplate = new DefaultSliderCaptchaTemplate("jpeg", "png", true); diff --git a/src/main/java/cloud/tianai/captcha/template/slider/DefaultResourceStore.java b/src/main/java/cloud/tianai/captcha/template/slider/DefaultResourceStore.java new file mode 100644 index 0000000..a4cf5a2 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/DefaultResourceStore.java @@ -0,0 +1,99 @@ +package cloud.tianai.captcha.template.slider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 15:43 + * @Description 默认的资源存储 + */ +public class DefaultResourceStore implements ResourceStore { + /** + * 模板资源. + */ + private List> templateResourceList = new ArrayList<>(2); + + /** + * resource. + */ + private List resourceList = new ArrayList<>(20); + + @Override + public void addResource(Resource resource) { + resourceList.add(resource); + } + + @Override + public void setResource(List resources) { + resourceList = new ArrayList<>(resources); + } + + @Override + public boolean deleteResource(Resource resource) { + return resourceList.remove(resource); + } + + @Override + public void clearResources() { + resourceList.clear(); + } + + @Override + public void addTemplate(Map template) { + templateResourceList.add(template); + } + + @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"); + } + return resourceList.get(index); + } + + @Override + public Map getTemplateByIndex(int index) { + if (index < 0 || index > templateResourceList.size() - 1) { + throw new IllegalArgumentException("错误的index"); + } + return templateResourceList.get(index); + } +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaResourceManager.java new file mode 100644 index 0000000..1cb4e11 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaResourceManager.java @@ -0,0 +1,106 @@ +package cloud.tianai.captcha.template.slider; + +import cloud.tianai.captcha.template.slider.provider.ClassPathResourceProvider; +import cloud.tianai.captcha.template.slider.provider.URLResourceProvider; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 15:35 + * @Description 默认的滑块验证码资源管理 + */ +public class DefaultSliderCaptchaResourceManager implements SliderCaptchaResourceManager { + + private ResourceStore resourceStore; + + private List resourceProviderList = new ArrayList<>(8); + + + public DefaultSliderCaptchaResourceManager() { + init(); + } + + public DefaultSliderCaptchaResourceManager(ResourceStore resourceStore) { + this.resourceStore = resourceStore; + init(); + } + + private void init() { + if (this.resourceStore == null) { + this.resourceStore = new DefaultResourceStore(); + } + // 注入一些默认的提供者 + registerResourceProvider(new URLResourceProvider()); + registerResourceProvider(new ClassPathResourceProvider()); + } + + @Override + public Map randomGetTemplate() { + int count = resourceStore.getTemplateCount(); + if (count < 1) { + throw new IllegalStateException("随机获取模板错误,store中模板为空"); + } + if (count == 1) { + return resourceStore.getTemplateByIndex(0); + } + int randomIndex = ThreadLocalRandom.current().nextInt(count); + return resourceStore.getTemplateByIndex(randomIndex); + } + + @Override + public Resource randomGetResource() { + int count = resourceStore.getResourceCount(); + if (count < 1) { + throw new IllegalStateException("随机获取资源错误,store中资源为空"); + } + if (count == 1) { + return resourceStore.getResourceByIndex(0); + } + int randomIndex = ThreadLocalRandom.current().nextInt(count); + return resourceStore.getResourceByIndex(randomIndex); + } + + @Override + public InputStream getResourceInputStream(Resource resource) { + for (ResourceProvider resourceProvider : resourceProviderList) { + if (resourceProvider.supported(resource.getType())) { + return resourceProvider.getResourceInputStream(resource); + } + } + throw new IllegalStateException("没有找到Resource [" + resource.getType() + "]对应的资源提供者"); + } + + @Override + public List listResourceProviders() { + return Collections.unmodifiableList(resourceProviderList); + } + + @Override + public void registerResourceProvider(ResourceProvider resourceProvider) { + deleteResourceProviderByName(resourceProvider.getName()); + resourceProviderList.add(resourceProvider); + } + + @Override + public boolean deleteResourceProviderByName(String name) { + return resourceProviderList.removeIf(r -> r.getName().equals(name)); + } + + @Override + public void setResourceStore(ResourceStore resourceStore) { + this.resourceStore = resourceStore; + } + + @Override + public ResourceStore getResourceStore() { + return resourceStore; + } + + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaTemplate.java b/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaTemplate.java index 4afc357..0616930 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaTemplate.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/DefaultSliderCaptchaTemplate.java @@ -1,5 +1,6 @@ package cloud.tianai.captcha.template.slider; +import cloud.tianai.captcha.template.slider.provider.ClassPathResourceProvider; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -11,9 +12,12 @@ import java.awt.image.ColorModel; import java.awt.image.PixelGrabber; import java.awt.image.WritableRaster; import java.io.ByteArrayOutputStream; +import java.io.InputStream; import java.net.URL; -import java.util.List; -import java.util.*; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ThreadLocalRandom; /** @@ -33,132 +37,38 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate { */ public static final String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template"; - public static final String ACTIVE_IMAGE_NAME = "active.png"; - public static final String FIXED_IMAGE_NAME = "fixed.png"; - public static final String MATRIX_IMAGE_NAME = "matrix.png"; - /** - * resource图片. - */ - private List resourceImageFiles = new ArrayList<>(20); - /** - * 模板图片. - */ - private List> templateImageFiles = new ArrayList<>(2); + private final SliderCaptchaResourceManager sliderCaptchaResourceManager; - protected String targetFormatName = "jpeg"; - protected String matrixFormatName = "png"; public void initDefaultResource() { + ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); // 添加一些系统的资源文件 - addResource(getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); + resourceStore.addResource(new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); // 添加一些系统的 模板文件 - Map template1 = new HashMap<>(4); - template1.put(ACTIVE_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png"))); - template1.put(FIXED_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png"))); - template1.put(MATRIX_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/matrix.png"))); - addTemplate(template1); + 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); - Map template2 = new HashMap<>(4); - template2.put(ACTIVE_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png"))); - template2.put(FIXED_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png"))); - template2.put(MATRIX_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/matrix.png"))); - addTemplate(template2); + 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); } - public DefaultSliderCaptchaTemplate(boolean initDefaultResource) { - // 加载系统资源文件 + public DefaultSliderCaptchaTemplate(SliderCaptchaResourceManager sliderCaptchaResourceManager, boolean initDefaultResource) { + this.sliderCaptchaResourceManager = sliderCaptchaResourceManager; if (initDefaultResource) { initDefaultResource(); } } - public DefaultSliderCaptchaTemplate(String targetFormatName, String matrixFormatName, boolean initDefaultResource) { - this.targetFormatName = targetFormatName; - this.matrixFormatName = matrixFormatName; - if (initDefaultResource) { - initDefaultResource(); - } - } - - public DefaultSliderCaptchaTemplate(String targetFormatName, - String matrixFormatName, - List r, - List> t, - boolean initDefaultResource) { - this.targetFormatName = targetFormatName; - this.matrixFormatName = matrixFormatName; - resourceImageFiles = r; - templateImageFiles = t; - if (initDefaultResource) { - initDefaultResource(); - } - } - - - public DefaultSliderCaptchaTemplate(List r, List> t, boolean initDefaultResource) { - resourceImageFiles = r; - templateImageFiles = t; - if (initDefaultResource) { - initDefaultResource(); - } - } - - - @Override - public void addResource(URL url) { - resourceImageFiles.remove(url); - resourceImageFiles.add(url); - } - - @Override - public void setResource(List resources) { - resourceImageFiles = resources; - } - - @Override - public void setTemplates(List> imageTemplates) { - templateImageFiles = imageTemplates; - } - - @Override - public void deleteResource(URL resource) { - resourceImageFiles.remove(resource); - } - - @Override - public List listResources() { - return Collections.unmodifiableList(resourceImageFiles); - } - - @Override - public void clearResources() { - resourceImageFiles.clear(); - } - - @Override - public void deleteTemplate(Map template) { - templateImageFiles.remove(template); - } - - @Override - public List> listTemplates() { - return Collections.unmodifiableList(templateImageFiles); - } - - @Override - public void clearTemplates() { - templateImageFiles.clear(); - } - - @Override - public void addTemplate(Map template) { - templateImageFiles.remove(template); - templateImageFiles.add(template); - } private static ClassLoader getClassLoader() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -173,27 +83,26 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate { @Override public SliderCaptchaInfo getSlideImageInfo() { - return getSlideImageInfo(this.targetFormatName, this.matrixFormatName); + return getSlideImageInfo("jpeg", "png"); } @SneakyThrows + @Override public SliderCaptchaInfo getSlideImageInfo(String targetFormatName, String matrixFormatName) { - if (resourceImageFiles.isEmpty() || templateImageFiles.isEmpty()) { - log.warn("滑块验证码生成失败, 资源或模板为空,不能进行生成, 资源文件列表长度: {}, 模板文件列表长度: {}", - resourceImageFiles.size(), templateImageFiles.size()); - return null; - } - URL resourceImage = getRandomResourceImage(); - Map templateImages = getRandomTemplateImages(); - BufferedImage cutBackground = warpFile2BufferedImage(resourceImage); + + Map templateImages = sliderCaptchaResourceManager.randomGetTemplate(); + Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(); + + + BufferedImage cutBackground = warpFile2BufferedImage(sliderCaptchaResourceManager.getResourceInputStream(resourceImage)); // 拷贝一份图片 BufferedImage targetBackground = deepCopyBufferedImage(cutBackground); - BufferedImage fixedTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, FIXED_IMAGE_NAME)); - BufferedImage activeTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, ACTIVE_IMAGE_NAME)); - BufferedImage matrixTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, MATRIX_IMAGE_NAME)); + BufferedImage fixedTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME)); + BufferedImage activeTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME)); + BufferedImage matrixTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME)); // BufferedImage cutTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, CUT_IMAGE_NAME)); // 获取随机的 x 和 y 轴 @@ -233,6 +142,11 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate { return newPercentage >= minTolerant && newPercentage <= maxTolerant; } + @Override + public SliderCaptchaResourceManager getSlideImageResourceManager() { + return sliderCaptchaResourceManager; + } + private String transformBase64(BufferedImage bufferedImage, String formatName) { byte[] data = null; @@ -383,21 +297,14 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate { g2d.dispose(); } - private URL getTemplateFile(Map templateImages, String imageName) { - URL url = templateImages.get(imageName); - if (url == null) { + private InputStream getTemplateFile(Map templateImages, String imageName) { + Resource resource = templateImages.get(imageName); + if (resource == null) { throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName)); } - return url; + return sliderCaptchaResourceManager.getResourceInputStream(resource); } - private Map getRandomTemplateImages() { - if (templateImageFiles.size() == 1) { - return templateImageFiles.get(0); - } - int templateNo = ThreadLocalRandom.current().nextInt(templateImageFiles.size()); - return templateImageFiles.get(templateNo); - } @SneakyThrows private static BufferedImage warpFile2BufferedImage(URL resourceImage) { @@ -407,23 +314,27 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate { return ImageIO.read(resourceImage); } - private URL getRandomResourceImage() { - if (resourceImageFiles.size() == 1) { - return resourceImageFiles.get(0); + @SneakyThrows + private static BufferedImage warpFile2BufferedImage(InputStream resource) { + if (resource == null) { + throw new IllegalArgumentException("包装文件到 BufferedImage 失败, file不能为空"); } - int targetNo = ThreadLocalRandom.current().nextInt(resourceImageFiles.size()); - return resourceImageFiles.get(targetNo); + return ImageIO.read(resource); } -// public static void main(String[] args) throws InterruptedException { -// DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(); -// // 生成滑块图片 -// SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo(); -// // 获取背景图片的base64 -// String backgroundImage = slideImageInfo.getBackgroundImage(); -// // 获取滑块图片 -// slideImageInfo.getSliderImage(); -// // 获取滑块被背景图片的百分比, (校验图片使用) -// Float xPercent = slideImageInfo.getXPercent(); -// } + + public static void main(String[] args) throws InterruptedException { + SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); + DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, true); + // 生成滑块图片 + SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo(); + // 获取背景图片的base64 + String backgroundImage = slideImageInfo.getBackgroundImage(); + // 获取滑块图片 + slideImageInfo.getSliderImage(); + // 获取滑块被背景图片的百分比, (校验图片使用) + Float xPercent = slideImageInfo.getXPercent(); + + System.out.println(slideImageInfo); + } } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/Resource.java b/src/main/java/cloud/tianai/captcha/template/slider/Resource.java new file mode 100644 index 0000000..e394b3c --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/Resource.java @@ -0,0 +1,18 @@ +package cloud.tianai.captcha.template.slider; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 15:15 + * @Description 资源对象 + */ +@Data +@AllArgsConstructor +public class Resource { + /** 类型. */ + private String type; + /** 数据,传输给 {@link ResourceProvider} 的参数 */ + public String data; +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/ResourceProvider.java b/src/main/java/cloud/tianai/captcha/template/slider/ResourceProvider.java new file mode 100644 index 0000000..c795575 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/ResourceProvider.java @@ -0,0 +1,34 @@ +package cloud.tianai.captcha.template.slider; + +import java.io.InputStream; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 15:07 + * @Description 资源提供者 + */ +public interface ResourceProvider { + + /** + * 获取资源 + * + * @param data data + * @return InputStream + */ + InputStream getResourceInputStream(Resource data); + + /** + * 是否支持 + * + * @param type type + * @return boolean + */ + boolean supported(String type); + + /** + * 放弃资源提供者名称 + * + * @return String + */ + String getName(); +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/ResourceStore.java b/src/main/java/cloud/tianai/captcha/template/slider/ResourceStore.java new file mode 100644 index 0000000..afa1fe5 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/ResourceStore.java @@ -0,0 +1,106 @@ +package cloud.tianai.captcha.template.slider; + +import java.util.List; +import java.util.Map; + +public interface ResourceStore { + + /** + * 添加资源 + * + * @param resource 资源 + */ + void addResource(Resource resource); + + /** + * 设置资源 + * + * @param resources resources + */ + void setResource(List resources); + + /** + * 删除资源 + * + * @param resource resource + */ + boolean deleteResource(Resource resource); + + /** + * 清除所有资源 + */ + 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(); + + + /** + * 获取所有资源对象 + * + * @return List + */ + List listResources(); + + /** + * 获取所有模板 + * + * @return List> + */ + List> listTemplates(); + + /** + * 获取资源总数 + * + * @return int + */ + int getResourceCount(); + + /** + * 获取模板count + * + * @return int + */ + int getTemplateCount(); + + /** + * 获取资源通过index + * + * @param index index + * @return Resource + */ + Resource getResourceByIndex(int index); + + /** + * 获取模板通过indx + * + * @param index index + * @return Map + */ + Map getTemplateByIndex(int index); + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaConstant.java b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaConstant.java new file mode 100644 index 0000000..d3d03b8 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaConstant.java @@ -0,0 +1,16 @@ +package cloud.tianai.captcha.template.slider; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 17:14 + * @Description 滑块验证码常量 + */ +public interface SliderCaptchaConstant { + + /** 模板滑块固定名称. */ + String TEMPLATE_ACTIVE_IMAGE_NAME = "active.png"; + /** 模板凹槽固定名称. */ + String TEMPLATE_FIXED_IMAGE_NAME = "fixed.png"; + /** 模板背景固定名称. */ + String TEMPLATE_MATRIX_IMAGE_NAME = "matrix.png"; +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaResourceManager.java b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaResourceManager.java new file mode 100644 index 0000000..6320535 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaResourceManager.java @@ -0,0 +1,71 @@ +package cloud.tianai.captcha.template.slider; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 15:26 + * @Description 滑块验证码图片资源管理器 + */ +public interface SliderCaptchaResourceManager { + + /** + * 随机获取某个模板 + * + * @return Map + */ + Map randomGetTemplate(); + + /** + * 随机获取某个资源对象 + * + * @return Resource + */ + Resource randomGetResource(); + + /** + * 获取真正的资源流通过资源对象 + * + * @param resource resource + * @return InputStream + */ + InputStream getResourceInputStream(Resource resource); + + /** + * 获取所有资源提供者 + * + * @return List + */ + List listResourceProviders(); + + /** + * 注册资源提供者 + * + * @param resourceProvider 资源提供者 + */ + void registerResourceProvider(ResourceProvider resourceProvider); + + /** + * 删除资源提供者 + * + * @param name 资源提供者名称 + * @return ResourceProvider + */ + boolean deleteResourceProviderByName(String name); + + /** + * 设置资源存储 + * + * @param resourceStore resourceStore + */ + void setResourceStore(ResourceStore resourceStore); + + /** + * 获取资源存储 + * + * @return ResourceStore + */ + ResourceStore getResourceStore(); +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaTemplate.java b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaTemplate.java index ffafc48..7092b44 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaTemplate.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaTemplate.java @@ -5,7 +5,7 @@ package cloud.tianai.captcha.template.slider; * @date 2020/10/19 18:37 * @Description 滑块验证码模板 */ -public interface SliderCaptchaTemplate extends SliderCaptchaResource { +public interface SliderCaptchaTemplate { /** * 获取滑块验证码 @@ -14,6 +14,16 @@ public interface SliderCaptchaTemplate extends SliderCaptchaResource { */ SliderCaptchaInfo getSlideImageInfo(); + + /** + * 获取滑块验证码 + * + * @param targetFormatName jpeg或者webp格式 + * @param matrixFormatName png或者webp格式 + * @return SliderCaptchaInfo + */ + SliderCaptchaInfo getSlideImageInfo(String targetFormatName, String matrixFormatName); + /** * 百分比对比 * @@ -22,4 +32,12 @@ public interface SliderCaptchaTemplate extends SliderCaptchaResource { * @return true 成功 false 失败 */ boolean percentageContrast(Float newPercentage, Float oriPercentage); + + /** + * 获取滑块验证码资源管理器 + * + * @return SliderCaptchaResourceManager + */ + SliderCaptchaResourceManager getSlideImageResourceManager(); + } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/provider/ClassPathResourceProvider.java b/src/main/java/cloud/tianai/captcha/template/slider/provider/ClassPathResourceProvider.java new file mode 100644 index 0000000..7b69f21 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/provider/ClassPathResourceProvider.java @@ -0,0 +1,42 @@ +package cloud.tianai.captcha.template.slider.provider; + +import cloud.tianai.captcha.template.slider.Resource; +import cloud.tianai.captcha.template.slider.ResourceProvider; + +import java.io.InputStream; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 16:07 + * @Description classPath + */ +public class ClassPathResourceProvider implements ResourceProvider { + + public static final String NAME = "classpath"; + + @Override + public InputStream getResourceInputStream(Resource data) { + return getClassLoader().getResourceAsStream(data.getData()); + } + + @Override + public boolean supported(String type) { + return NAME.equalsIgnoreCase(type); + } + + @Override + public String getName() { + return NAME; + } + + private static ClassLoader getClassLoader() { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = ClassPathResourceProvider.getClassLoader(); + } + if (classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + return classLoader; + } +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/provider/URLResourceProvider.java b/src/main/java/cloud/tianai/captcha/template/slider/provider/URLResourceProvider.java new file mode 100644 index 0000000..702c277 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/provider/URLResourceProvider.java @@ -0,0 +1,35 @@ +package cloud.tianai.captcha.template.slider.provider; + +import cloud.tianai.captcha.template.slider.Resource; +import cloud.tianai.captcha.template.slider.ResourceProvider; +import lombok.SneakyThrows; + +import java.io.InputStream; +import java.net.URL; + +/** + * @Author: 天爱有情 + * @date 2021/8/7 16:05 + * @Description url + */ +public class URLResourceProvider implements ResourceProvider { + + public static final String NAME = "URL"; + + @SneakyThrows + @Override + public InputStream getResourceInputStream(Resource data) { + URL url = new URL(data.getData()); + return url.openStream(); + } + + @Override + public boolean supported(String type) { + return NAME.equalsIgnoreCase(type); + } + + @Override + public String getName() { + return NAME; + } +}