U 修改资源读取和存储模块

升级版本为1.2
This commit is contained in:
liushaofeng
2021-08-07 17:16:57 +08:00
parent c5260356de
commit b56919f17d
14 changed files with 702 additions and 223 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>cloud.tianai.captcha</groupId> <groupId>cloud.tianai.captcha</groupId>
<artifactId>tianai-captcha</artifactId> <artifactId>tianai-captcha</artifactId>
<version>1.1</version> <version>1.2</version>
<name>tianai-captcha</name> <name>tianai-captcha</name>
<description>滑块验证码</description> <description>滑块验证码</description>
+64 -14
View File
@@ -9,19 +9,21 @@
- java获取滑块验证码例子 - java获取滑块验证码例子
## 快速上手
1. 导入xml
```xml ```xml
<!-- maven 导入 --> <!-- maven 导入 -->
<dependency> <dependency>
<groupId>cloud.tianai.captcha</groupId> <groupId>cloud.tianai.captcha</groupId>
<artifactId>tianai-captcha</artifactId> <artifactId>tianai-captcha</artifactId>
<version>1.1</version> <version>1.2</version>
</dependency> </dependency>
``` ```
2. 使用 `SliderCaptchaTemplate`获取滑块验证码
```java ```java
public static void main(String[] args) { public static void main(String[] args) throws InterruptedException {
SliderCaptchaTemplate sliderCaptchaTemplate = new SliderCaptchaTemplate(); SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager();
DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, true);
// 生成滑块图片 // 生成滑块图片
SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo(); SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo();
// 获取背景图片的base64 // 获取背景图片的base64
@@ -30,18 +32,66 @@ public static void main(String[] args) {
slideImageInfo.getSliderImage(); slideImageInfo.getSliderImage();
// 获取滑块被背景图片的百分比, (校验图片使用) // 获取滑块被背景图片的百分比, (校验图片使用)
Float xPercent = slideImageInfo.getXPercent(); Float xPercent = slideImageInfo.getXPercent();
System.out.println(slideImageInfo);
} }
``` ```
- 添加自定义背景图片例子 # 常用接口
- 添加自定义图片资源
```java ```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 ```java
Map<String, URL> template1 = new HashMap<>(4); ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore();=
template1.put(ACTIVE_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png"))); Map<String, Resource> template1 = new HashMap<>(4);
template1.put(CUT_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/cut.png"))); template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME,"/active.png"));
template1.put(FIXED_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png"))); template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/fixed.png"));
template1.put(MATRIX_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/matrix.png"))); template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/matrix.png"));
addTemplate(template1); 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());
``` ```
@@ -3,9 +3,6 @@ package cloud.tianai.captcha.template.slider;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; 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.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -25,12 +22,23 @@ public class CacheSliderCaptchaTemplate implements SliderCaptchaTemplate {
private AtomicInteger pos = new AtomicInteger(0); private AtomicInteger pos = new AtomicInteger(0);
private SliderCaptchaTemplate target; private SliderCaptchaTemplate target;
private int size; private int size;
/** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */
private int waitTime = 1000;
/** 调度器检查缓存的间隔时间. */
private int period = 100;
public CacheSliderCaptchaTemplate(SliderCaptchaTemplate target, int size) { public CacheSliderCaptchaTemplate(SliderCaptchaTemplate target, int size) {
this.target = target; this.target = target;
this.size = size; 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(); pos.incrementAndGet();
} }
} else { } else {
// 休眠500毫秒 sleep();
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException ignored) {
} }
} }
}
} catch (Exception e) { } catch (Exception e) {
// cache所有 // cache所有
log.error("缓存队列扫描时出错, ex", e); log.error("缓存队列扫描时出错, ex", e);
// 休眠
sleep();
} }
}, 0, 100, TimeUnit.MILLISECONDS); }, 0, period, TimeUnit.MILLISECONDS);
log.info("缓存滑块验证码调度器初始化完成: size:{}", size); log.info("缓存滑块验证码调度器初始化完成: size:{}", size);
} }
private void sleep() {
try {
TimeUnit.MILLISECONDS.sleep(waitTime);
} catch (InterruptedException ignored) {
}
}
@SneakyThrows @SneakyThrows
@Override @Override
public SliderCaptchaInfo getSlideImageInfo() { public SliderCaptchaInfo getSlideImageInfo() {
@@ -88,60 +100,21 @@ public class CacheSliderCaptchaTemplate implements SliderCaptchaTemplate {
} }
@Override @Override
public void addResource(URL url) { public SliderCaptchaInfo getSlideImageInfo(String targetFormatName, String matrixFormatName) {
target.addResource(url); return target.getSlideImageInfo(targetFormatName, matrixFormatName);
} }
@Override
public void addTemplate(Map<String, URL> template) {
target.addTemplate(template);
}
@Override
public void setResource(List<URL> resources) {
target.setResource(resources);
}
@Override
public void setTemplates(List<Map<String, URL>> imageTemplates) {
target.setTemplates(imageTemplates);
}
@Override
public void deleteResource(URL resource) {
target.deleteResource(resource);
}
@Override
public List<URL> listResources() {
return target.listResources();
}
@Override
public void clearResources() {
target.clearResources();
}
@Override
public void deleteTemplate(Map<String, URL> template) {
target.deleteTemplate(template);
}
@Override
public List<Map<String, URL>> listTemplates() {
return target.listTemplates();
}
@Override
public void clearTemplates() {
target.clearTemplates();
}
@Override @Override
public boolean percentageContrast(Float newPercentage, Float oriPercentage) { public boolean percentageContrast(Float newPercentage, Float oriPercentage) {
return target.percentageContrast(newPercentage, oriPercentage); return target.percentageContrast(newPercentage, oriPercentage);
} }
@Override
public SliderCaptchaResourceManager getSlideImageResourceManager() {
return target.getSlideImageResourceManager();
}
// public static void main(String[] args) throws InterruptedException { // public static void main(String[] args) throws InterruptedException {
// SliderCaptchaTemplate captchaTemplate = new DefaultSliderCaptchaTemplate("jpeg", "png", true); // SliderCaptchaTemplate captchaTemplate = new DefaultSliderCaptchaTemplate("jpeg", "png", true);
@@ -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<Map<String, Resource>> templateResourceList = new ArrayList<>(2);
/**
* resource.
*/
private List<Resource> resourceList = new ArrayList<>(20);
@Override
public void addResource(Resource resource) {
resourceList.add(resource);
}
@Override
public void setResource(List<Resource> 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<String, Resource> template) {
templateResourceList.add(template);
}
@Override
public void setTemplates(List<Map<String, Resource>> templateResource) {
templateResourceList = new ArrayList<>(templateResource);
}
@Override
public void deleteTemplate(Map<String, Resource> template) {
templateResourceList.remove(template);
}
@Override
public void clearTemplates() {
templateResourceList.clear();
}
@Override
public List<Resource> listResources() {
return Collections.unmodifiableList(resourceList);
}
@Override
public List<Map<String, Resource>> 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<String, Resource> getTemplateByIndex(int index) {
if (index < 0 || index > templateResourceList.size() - 1) {
throw new IllegalArgumentException("错误的index");
}
return templateResourceList.get(index);
}
}
@@ -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<ResourceProvider> 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<String, Resource> 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<ResourceProvider> 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;
}
}
@@ -1,5 +1,6 @@
package cloud.tianai.captcha.template.slider; package cloud.tianai.captcha.template.slider;
import cloud.tianai.captcha.template.slider.provider.ClassPathResourceProvider;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -11,9 +12,12 @@ import java.awt.image.ColorModel;
import java.awt.image.PixelGrabber; import java.awt.image.PixelGrabber;
import java.awt.image.WritableRaster; import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.ArrayList;
import java.util.*; import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom; 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 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";
/** private final SliderCaptchaResourceManager sliderCaptchaResourceManager;
* resource图片.
*/
private List<URL> resourceImageFiles = new ArrayList<>(20);
/**
* 模板图片.
*/
private List<Map<String, URL>> templateImageFiles = new ArrayList<>(2);
protected String targetFormatName = "jpeg";
protected String matrixFormatName = "png";
public void initDefaultResource() { 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<String, URL> template1 = new HashMap<>(4); Map<String, Resource> template1 = new HashMap<>(4);
template1.put(ACTIVE_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png"))); template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, 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(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, 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"))); template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/matrix.png")));
addTemplate(template1); resourceStore.addTemplate(template1);
Map<String, URL> template2 = new HashMap<>(4); Map<String, Resource> template2 = new HashMap<>(4);
template2.put(ACTIVE_IMAGE_NAME, getClassLoader().getResource(DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png"))); template2.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, 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(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, 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"))); template2.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/matrix.png")));
addTemplate(template2); resourceStore.addTemplate(template2);
} }
public DefaultSliderCaptchaTemplate(boolean initDefaultResource) { public DefaultSliderCaptchaTemplate(SliderCaptchaResourceManager sliderCaptchaResourceManager, boolean initDefaultResource) {
// 加载系统资源文件 this.sliderCaptchaResourceManager = sliderCaptchaResourceManager;
if (initDefaultResource) { if (initDefaultResource) {
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<URL> r,
List<Map<String, URL>> t,
boolean initDefaultResource) {
this.targetFormatName = targetFormatName;
this.matrixFormatName = matrixFormatName;
resourceImageFiles = r;
templateImageFiles = t;
if (initDefaultResource) {
initDefaultResource();
}
}
public DefaultSliderCaptchaTemplate(List<URL> r, List<Map<String, URL>> 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<URL> resources) {
resourceImageFiles = resources;
}
@Override
public void setTemplates(List<Map<String, URL>> imageTemplates) {
templateImageFiles = imageTemplates;
}
@Override
public void deleteResource(URL resource) {
resourceImageFiles.remove(resource);
}
@Override
public List<URL> listResources() {
return Collections.unmodifiableList(resourceImageFiles);
}
@Override
public void clearResources() {
resourceImageFiles.clear();
}
@Override
public void deleteTemplate(Map<String, URL> template) {
templateImageFiles.remove(template);
}
@Override
public List<Map<String, URL>> listTemplates() {
return Collections.unmodifiableList(templateImageFiles);
}
@Override
public void clearTemplates() {
templateImageFiles.clear();
}
@Override
public void addTemplate(Map<String, URL> template) {
templateImageFiles.remove(template);
templateImageFiles.add(template);
}
private static ClassLoader getClassLoader() { private static ClassLoader getClassLoader() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
@@ -173,27 +83,26 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate {
@Override @Override
public SliderCaptchaInfo getSlideImageInfo() { public SliderCaptchaInfo getSlideImageInfo() {
return getSlideImageInfo(this.targetFormatName, this.matrixFormatName); return getSlideImageInfo("jpeg", "png");
} }
@SneakyThrows @SneakyThrows
@Override
public SliderCaptchaInfo getSlideImageInfo(String targetFormatName, String matrixFormatName) { public SliderCaptchaInfo getSlideImageInfo(String targetFormatName, String matrixFormatName) {
if (resourceImageFiles.isEmpty() || templateImageFiles.isEmpty()) {
log.warn("滑块验证码生成失败, 资源或模板为空,不能进行生成, 资源文件列表长度: {}, 模板文件列表长度: {}",
resourceImageFiles.size(), templateImageFiles.size());
return null;
}
URL resourceImage = getRandomResourceImage();
Map<String, URL> templateImages = getRandomTemplateImages();
BufferedImage cutBackground = warpFile2BufferedImage(resourceImage);
Map<String, Resource> templateImages = sliderCaptchaResourceManager.randomGetTemplate();
Resource resourceImage = sliderCaptchaResourceManager.randomGetResource();
BufferedImage cutBackground = warpFile2BufferedImage(sliderCaptchaResourceManager.getResourceInputStream(resourceImage));
// 拷贝一份图片 // 拷贝一份图片
BufferedImage targetBackground = deepCopyBufferedImage(cutBackground); BufferedImage targetBackground = deepCopyBufferedImage(cutBackground);
BufferedImage fixedTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, FIXED_IMAGE_NAME)); BufferedImage fixedTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME));
BufferedImage activeTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, ACTIVE_IMAGE_NAME)); BufferedImage activeTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME));
BufferedImage matrixTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, MATRIX_IMAGE_NAME)); BufferedImage matrixTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME));
// BufferedImage cutTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, CUT_IMAGE_NAME)); // BufferedImage cutTemplate = warpFile2BufferedImage(getTemplateFile(templateImages, CUT_IMAGE_NAME));
// 获取随机的 x 和 y 轴 // 获取随机的 x 和 y 轴
@@ -233,6 +142,11 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate {
return newPercentage >= minTolerant && newPercentage <= maxTolerant; return newPercentage >= minTolerant && newPercentage <= maxTolerant;
} }
@Override
public SliderCaptchaResourceManager getSlideImageResourceManager() {
return sliderCaptchaResourceManager;
}
private String transformBase64(BufferedImage bufferedImage, String formatName) { private String transformBase64(BufferedImage bufferedImage, String formatName) {
byte[] data = null; byte[] data = null;
@@ -383,21 +297,14 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate {
g2d.dispose(); g2d.dispose();
} }
private URL getTemplateFile(Map<String, URL> templateImages, String imageName) { private InputStream getTemplateFile(Map<String, Resource> templateImages, String imageName) {
URL url = templateImages.get(imageName); Resource resource = templateImages.get(imageName);
if (url == null) { if (resource == null) {
throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName)); throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName));
} }
return url; return sliderCaptchaResourceManager.getResourceInputStream(resource);
} }
private Map<String, URL> getRandomTemplateImages() {
if (templateImageFiles.size() == 1) {
return templateImageFiles.get(0);
}
int templateNo = ThreadLocalRandom.current().nextInt(templateImageFiles.size());
return templateImageFiles.get(templateNo);
}
@SneakyThrows @SneakyThrows
private static BufferedImage warpFile2BufferedImage(URL resourceImage) { private static BufferedImage warpFile2BufferedImage(URL resourceImage) {
@@ -407,23 +314,27 @@ public class DefaultSliderCaptchaTemplate implements SliderCaptchaTemplate {
return ImageIO.read(resourceImage); return ImageIO.read(resourceImage);
} }
private URL getRandomResourceImage() { @SneakyThrows
if (resourceImageFiles.size() == 1) { private static BufferedImage warpFile2BufferedImage(InputStream resource) {
return resourceImageFiles.get(0); if (resource == null) {
throw new IllegalArgumentException("包装文件到 BufferedImage 失败, file不能为空");
} }
int targetNo = ThreadLocalRandom.current().nextInt(resourceImageFiles.size()); return ImageIO.read(resource);
return resourceImageFiles.get(targetNo);
} }
// public static void main(String[] args) throws InterruptedException {
// DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(); public static void main(String[] args) throws InterruptedException {
// // 生成滑块图片 SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager();
// SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo(); DefaultSliderCaptchaTemplate sliderCaptchaTemplate = new DefaultSliderCaptchaTemplate(sliderCaptchaResourceManager, true);
// // 获取背景图片的base64 // 生成滑块图片
// String backgroundImage = slideImageInfo.getBackgroundImage(); SliderCaptchaInfo slideImageInfo = sliderCaptchaTemplate.getSlideImageInfo();
// // 获取滑块图片 // 获取背景图片的base64
// slideImageInfo.getSliderImage(); String backgroundImage = slideImageInfo.getBackgroundImage();
// // 获取滑块被背景图片的百分比, (校验图片使用) // 获取滑块图片
// Float xPercent = slideImageInfo.getXPercent(); slideImageInfo.getSliderImage();
// } // 获取滑块被背景图片的百分比, (校验图片使用)
Float xPercent = slideImageInfo.getXPercent();
System.out.println(slideImageInfo);
}
} }
@@ -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;
}
@@ -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();
}
@@ -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<Resource> resources);
/**
* 删除资源
*
* @param resource resource
*/
boolean deleteResource(Resource resource);
/**
* 清除所有资源
*/
void clearResources();
/**
* 添加模板
*
* @param template template
*/
void addTemplate(Map<String, Resource> template);
/**
* 设置模板
*
* @param templateResource templateResource
*/
void setTemplates(List<Map<String, Resource>> templateResource);
/**
* 删除模板
*
* @param template template
*/
void deleteTemplate(Map<String, Resource> template);
/**
* 清除所有模板
*/
void clearTemplates();
/**
* 获取所有资源对象
*
* @return List<Resource>
*/
List<Resource> listResources();
/**
* 获取所有模板
*
* @return List<Map < String, Resource>>
*/
List<Map<String, Resource>> 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<String, Resource>
*/
Map<String, Resource> getTemplateByIndex(int index);
}
@@ -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";
}
@@ -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<String, Resource>
*/
Map<String, Resource> randomGetTemplate();
/**
* 随机获取某个资源对象
*
* @return Resource
*/
Resource randomGetResource();
/**
* 获取真正的资源流通过资源对象
*
* @param resource resource
* @return InputStream
*/
InputStream getResourceInputStream(Resource resource);
/**
* 获取所有资源提供者
*
* @return List<ResourceProvider>
*/
List<ResourceProvider> 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();
}
@@ -5,7 +5,7 @@ package cloud.tianai.captcha.template.slider;
* @date 2020/10/19 18:37 * @date 2020/10/19 18:37
* @Description 滑块验证码模板 * @Description 滑块验证码模板
*/ */
public interface SliderCaptchaTemplate extends SliderCaptchaResource { public interface SliderCaptchaTemplate {
/** /**
* 获取滑块验证码 * 获取滑块验证码
@@ -14,6 +14,16 @@ public interface SliderCaptchaTemplate extends SliderCaptchaResource {
*/ */
SliderCaptchaInfo getSlideImageInfo(); 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 失败 * @return true 成功 false 失败
*/ */
boolean percentageContrast(Float newPercentage, Float oriPercentage); boolean percentageContrast(Float newPercentage, Float oriPercentage);
/**
* 获取滑块验证码资源管理器
*
* @return SliderCaptchaResourceManager
*/
SliderCaptchaResourceManager getSlideImageResourceManager();
} }
@@ -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;
}
}
@@ -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;
}
}