1.5.0正式版
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cloud.tianai.captcha</groupId>
|
||||
<artifactId>tianai-captcha</artifactId>
|
||||
<version>1.5.0.beta</version>
|
||||
<version>1.5.0</version>
|
||||
|
||||
<name>tianai-captcha</name>
|
||||
<description>行为验证码</description>
|
||||
|
||||
@@ -12,6 +12,7 @@ import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.generator.impl.CacheImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
@@ -51,7 +52,7 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
||||
ImageCaptchaProperties prop,
|
||||
CaptchaInterceptor captchaInterceptor) {
|
||||
this.prop = prop;
|
||||
setImageCaptchaGenerator(captchaGenerator);
|
||||
|
||||
setImageCaptchaValidator(imageCaptchaValidator);
|
||||
setCacheStore(cacheStore);
|
||||
// 默认过期时间
|
||||
@@ -65,6 +66,14 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
||||
this.captchaInterceptor = captchaInterceptor;
|
||||
}
|
||||
captchaGenerator.setInterceptor(this.captchaInterceptor);
|
||||
if (prop.isLocalCacheEnabled()) {
|
||||
captchaGenerator = new CacheImageCaptchaGenerator(captchaGenerator,
|
||||
prop.getLocalCacheSize(), prop.getLocalCacheWaitTime(),
|
||||
prop.getLocalCachePeriod(), prop.getLocalCacheExpireTime());
|
||||
}
|
||||
// 初始化生成器
|
||||
captchaGenerator.init();
|
||||
setImageCaptchaGenerator(captchaGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,7 @@ package cloud.tianai.captcha.application;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -15,5 +15,12 @@ public class ImageCaptchaProperties {
|
||||
/** 过期key prefix. */
|
||||
private String prefix = "captcha";
|
||||
/** 过期时间. */
|
||||
private Map<String, Long> expire = Collections.emptyMap();
|
||||
private Map<String, Long> expire = new HashMap<>();
|
||||
|
||||
// 本地提前缓存
|
||||
private boolean localCacheEnabled = false;
|
||||
private int localCacheSize = 10;
|
||||
private int localCacheWaitTime = 1000;
|
||||
private int localCachePeriod = 5000;
|
||||
private Long localCacheExpireTime;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
package cloud.tianai.captcha.application;
|
||||
|
||||
import cloud.tianai.captcha.cache.CacheStore;
|
||||
import cloud.tianai.captcha.cache.impl.LocalCacheStore;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
import cloud.tianai.captcha.generator.common.FontWrapper;
|
||||
import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.DefaultBuiltInResources;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore;
|
||||
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
|
||||
import cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2024/7/14 16:41
|
||||
* @Description 一个构建ImageCaptchaApplication的工具, 免去一些繁琐的配置,方便新手用户一键使用
|
||||
*/
|
||||
public class TACBuilder {
|
||||
|
||||
private CacheStore cacheStore;
|
||||
private ImageCaptchaGenerator generator;
|
||||
private ImageCaptchaValidator validator;
|
||||
private CaptchaInterceptor interceptor = EmptyCaptchaInterceptor.INSTANCE;
|
||||
private ImageCaptchaProperties prop = new ImageCaptchaProperties();
|
||||
private ResourceStore resourceStore;
|
||||
private ImageTransform imageTransform;
|
||||
private List<FontWrapper> fontWrappers = new ArrayList<>();
|
||||
|
||||
public static TACBuilder builder() {
|
||||
TACBuilder builder = new TACBuilder();
|
||||
// 默认设置本地的
|
||||
LocalMemoryResourceStore resourceStore = new LocalMemoryResourceStore();
|
||||
builder.resourceStore = resourceStore;
|
||||
builder.prop = new ImageCaptchaProperties();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private TACBuilder() {
|
||||
}
|
||||
|
||||
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
||||
DefaultBuiltInResources defaultBuiltInResources = new DefaultBuiltInResources(defaultPathPrefix);
|
||||
defaultBuiltInResources.addDefaultTemplate(resourceStore);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder addDefaultTemplate() {
|
||||
return addDefaultTemplate(DefaultBuiltInResources.PATH_PREFIX);
|
||||
}
|
||||
|
||||
public TACBuilder setCacheStore(CacheStore cacheStore) {
|
||||
this.cacheStore = cacheStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder setGenerator(ImageCaptchaGenerator generator) {
|
||||
this.generator = generator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder setValidator(ImageCaptchaValidator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder setInterceptor(CaptchaInterceptor interceptor) {
|
||||
this.interceptor = interceptor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder addFont(FontWrapper fontWrapper) {
|
||||
this.fontWrappers.add(fontWrapper);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder addFont(Font font) {
|
||||
return addFont(new FontWrapper(font));
|
||||
}
|
||||
|
||||
|
||||
public TACBuilder cached(int size, int waitTime, int period, Long expireTime) {
|
||||
prop.setLocalCacheEnabled(true);
|
||||
prop.setLocalCacheSize(size);
|
||||
prop.setLocalCacheWaitTime(waitTime);
|
||||
prop.setLocalCachePeriod(period);
|
||||
prop.setLocalCacheExpireTime(expireTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder prefix(String prefix) {
|
||||
this.prop.setPrefix(prefix);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder expire(String captchaType, Long expireTime) {
|
||||
prop.getExpire().put(captchaType, expireTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder setProp(ImageCaptchaProperties prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder setResourceStore(ResourceStore resourceStore) {
|
||||
this.resourceStore = resourceStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TACBuilder addResource(String captchaType, Resource imageResource) {
|
||||
this.resourceStore.addResource(captchaType, imageResource);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder addTemplate(String captchaType, ResourceMap resourceMap) {
|
||||
this.resourceStore.addTemplate(captchaType, resourceMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TACBuilder setTransform(ImageTransform imageTransform) {
|
||||
this.imageTransform = imageTransform;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImageCaptchaApplication build() {
|
||||
if (cacheStore == null) {
|
||||
cacheStore = new LocalCacheStore();
|
||||
}
|
||||
if (generator == null) {
|
||||
DefaultImageCaptchaResourceManager resourceManager = new DefaultImageCaptchaResourceManager(resourceStore);
|
||||
generator = new MultiImageCaptchaGenerator(resourceManager, imageTransform);
|
||||
}
|
||||
if (generator instanceof MultiImageCaptchaGenerator) {
|
||||
if (CollectionUtils.isEmpty(fontWrappers)) {
|
||||
// 添加默认字体
|
||||
try {
|
||||
ClassPathResourceProvider resourceProvider = new ClassPathResourceProvider();
|
||||
InputStream stream = resourceProvider.getResourceInputStream(new Resource("classpath", "META-INF/fonts/SIMSUN.TTC"));
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, stream);
|
||||
stream.close();
|
||||
fontWrappers.add(new FontWrapper(font));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("读取默认字体包报错",e);
|
||||
}
|
||||
}
|
||||
((MultiImageCaptchaGenerator) generator).setFontWrappers(fontWrappers);
|
||||
}
|
||||
if (validator == null) {
|
||||
validator = new SimpleImageCaptchaValidator();
|
||||
}
|
||||
if (interceptor == null) {
|
||||
interceptor = EmptyCaptchaInterceptor.INSTANCE;
|
||||
}
|
||||
|
||||
DefaultImageCaptchaApplication application = new DefaultImageCaptchaApplication(generator, validator, cacheStore, prop, interceptor);
|
||||
return application;
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCaptchaGenerator init(boolean initDefaultResource) {
|
||||
public ImageCaptchaGenerator init() {
|
||||
if (init) {
|
||||
return this;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
if (getImageTransform() == null) {
|
||||
setImageTransform(new Base64ImageTransform());
|
||||
}
|
||||
doInit(initDefaultResource);
|
||||
doInit();
|
||||
} catch (Exception e) {
|
||||
init = false;
|
||||
log.error("[{}]初始化失败,ex", this.getClass().getSimpleName(), e);
|
||||
@@ -237,9 +237,8 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param initDefaultResource 是否初始化默认资源
|
||||
*/
|
||||
protected abstract void doInit(boolean initDefaultResource);
|
||||
protected abstract void doInit();
|
||||
|
||||
/**
|
||||
* 生成验证码方法
|
||||
|
||||
@@ -17,10 +17,9 @@ public interface ImageCaptchaGenerator {
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param initDefaultResource 是否初始化默认资源
|
||||
* @return ImageCaptchaGenerator
|
||||
*/
|
||||
ImageCaptchaGenerator init(boolean initDefaultResource);
|
||||
ImageCaptchaGenerator init();
|
||||
|
||||
/**
|
||||
* 生成验证码图片
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package cloud.tianai.captcha.generator;
|
||||
|
||||
import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/4/24 15:11
|
||||
* @Description 图片验证码后处理器
|
||||
*/
|
||||
public interface ImageCaptchaPostProcessor {
|
||||
|
||||
/**
|
||||
* 在生成验证码核心逻辑之前调用, 用于拦截验证码生成、限流、自定义返回数据等处理
|
||||
*
|
||||
* @param captchaExchange 传输数据
|
||||
* @param generator generator
|
||||
* @return ImageCaptchaInfo
|
||||
*/
|
||||
default ImageCaptchaInfo beforeGenerateCaptchaImage(CaptchaExchange captchaExchange, ImageCaptchaGenerator generator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在执行包装 ImageCaptchaInfo 核心逻辑之前处理
|
||||
*
|
||||
* @param captchaExchange 传输数据
|
||||
* @param generator generator
|
||||
*/
|
||||
default void beforeWrapImageCaptchaInfo(CaptchaExchange captchaExchange, ImageCaptchaGenerator generator) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 在执行包装 ImageCaptchaInfo 核心逻辑之后处理
|
||||
*
|
||||
* @param captchaExchange captchaExchange
|
||||
* @param imageCaptchaInfo imageCaptchaInfo
|
||||
* @param generator generator
|
||||
*/
|
||||
default void afterGenerateCaptchaImage(CaptchaExchange captchaExchange, ImageCaptchaInfo imageCaptchaInfo, ImageCaptchaGenerator generator) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,22 @@ import java.awt.*;
|
||||
@AllArgsConstructor
|
||||
public class FontWrapper {
|
||||
private Font font;
|
||||
private float currentFontTopCoef;
|
||||
private Float currentFontTopCoef;
|
||||
|
||||
public FontWrapper(Font font) {
|
||||
this(font, 70);
|
||||
}
|
||||
|
||||
public FontWrapper(Font font, int fontSize) {
|
||||
this.font = font;
|
||||
this.font = font.deriveFont(Font.BOLD, fontSize);
|
||||
}
|
||||
|
||||
public float getCurrentFontTopCoef() {
|
||||
if (currentFontTopCoef != null) {
|
||||
return currentFontTopCoef;
|
||||
}
|
||||
currentFontTopCoef = 0.14645833f * font.getSize() + 0.39583333f;
|
||||
return currentFontTopCoef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCaptchaGenerator init(boolean initDefaultResource) {
|
||||
ImageCaptchaGenerator captchaGenerator = target.init(initDefaultResource);
|
||||
public ImageCaptchaGenerator init() {
|
||||
ImageCaptchaGenerator captchaGenerator = target.init();
|
||||
// 初始化缓存
|
||||
init(size);;
|
||||
return captchaGenerator;
|
||||
|
||||
@@ -5,6 +5,7 @@ import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
import cloud.tianai.captcha.generator.common.FontWrapper;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.CaptchaExchange;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
@@ -14,6 +15,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -28,13 +30,14 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
|
||||
protected Map<String, ImageCaptchaGenerator> imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4);
|
||||
protected Map<String, ImageCaptchaGeneratorProvider> imageCaptchaGeneratorProviderMap = new HashMap<>(4);
|
||||
|
||||
// 点选类验证码字体
|
||||
@Setter
|
||||
@Getter
|
||||
protected List<FontWrapper> fontWrappers;
|
||||
@Setter
|
||||
@Getter
|
||||
private String defaultCaptcha = SLIDER;
|
||||
|
||||
protected boolean initDefaultResource = false;
|
||||
|
||||
public MultiImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
|
||||
super(imageCaptchaResourceManager);
|
||||
}
|
||||
@@ -45,9 +48,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit(boolean initDefaultResource) {
|
||||
this.initDefaultResource = initDefaultResource;
|
||||
|
||||
protected void doInit() {
|
||||
// 滑块验证码
|
||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(SLIDER, StandardSliderImageCaptchaGenerator::new));
|
||||
// 旋转验证码
|
||||
@@ -55,7 +56,8 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
// 拼接验证码
|
||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new));
|
||||
// 点选文字验证码
|
||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, StandardWordClickImageCaptchaGenerator::new));
|
||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, (r, t, i) ->
|
||||
new StandardWordClickImageCaptchaGenerator(r, t, i, fontWrappers)));
|
||||
}
|
||||
|
||||
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
|
||||
@@ -110,7 +112,7 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("生成验证码失败,错误的type类型:" + t);
|
||||
}
|
||||
return provider.get(getImageResourceManager(), getImageTransform(), getInterceptor()).init(initDefaultResource);
|
||||
return provider.get(getImageResourceManager(), getImageTransform(), getInterceptor()).init();
|
||||
});
|
||||
return imageCaptchaGenerator;
|
||||
}
|
||||
|
||||
@@ -6,15 +6,11 @@ import cloud.tianai.captcha.generator.ImageTransform;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.*;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
|
||||
import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.concatImage;
|
||||
import static cloud.tianai.captcha.generator.common.util.CaptchaImageUtils.splitImage;
|
||||
|
||||
@@ -41,17 +37,9 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit(boolean initDefaultResource) {
|
||||
if (initDefaultResource) {
|
||||
initDefaultResource();
|
||||
}
|
||||
protected void doInit() {
|
||||
}
|
||||
|
||||
public void initDefaultResource() {
|
||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||
// 添加一些系统的资源文件
|
||||
resourceStore.addResource(CaptchaTypeConstant.CONCAT, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
package cloud.tianai.captcha.generator.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.*;
|
||||
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.*;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/4/22 16:43
|
||||
@@ -46,25 +41,10 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
setImageTransform(imageTransform);
|
||||
setInterceptor(interceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit(boolean initDefaultResource) {
|
||||
if (initDefaultResource) {
|
||||
initDefaultResource();
|
||||
}
|
||||
protected void doInit() {
|
||||
}
|
||||
|
||||
public void initDefaultResource() {
|
||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||
// 添加一些系统的资源文件
|
||||
resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
|
||||
// 添加一些系统的 模板文件
|
||||
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png")));
|
||||
template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGenerateCaptchaImage(CaptchaExchange captchaExchange) {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package cloud.tianai.captcha.generator.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||
import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.*;
|
||||
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -19,8 +16,6 @@ import java.awt.image.BufferedImage;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.*;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @Date 2020/5/29 8:06
|
||||
@@ -57,10 +52,8 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
|
||||
|
||||
@Override
|
||||
protected void doInit(boolean initDefaultResource) {
|
||||
if (initDefaultResource) {
|
||||
initDefaultResource();
|
||||
}
|
||||
protected void doInit() {
|
||||
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -169,24 +162,4 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
// 左边混淆
|
||||
return randomInt(slWidth, sliderX - slWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化默认资源
|
||||
*/
|
||||
public void initDefaultResource() {
|
||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||
// 添加一些系统的资源文件
|
||||
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
|
||||
// 添加一些系统的 模板文件
|
||||
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png")));
|
||||
template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template1);
|
||||
|
||||
ResourceMap template2 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template2.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png")));
|
||||
template2.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cloud.tianai.captcha.generator.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.common.util.FontUtils;
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
@@ -9,26 +10,18 @@ import cloud.tianai.captcha.generator.common.model.dto.*;
|
||||
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/4/27 11:46
|
||||
@@ -108,27 +101,11 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows({IOException.class, FontFormatException.class})
|
||||
protected void doInit(boolean initDefaultResource) {
|
||||
protected void doInit() {
|
||||
if (CollectionUtils.isEmpty(fonts)) {
|
||||
// 使用默认字体
|
||||
Resource fontResource = new Resource(null, "META-INF/fonts/SIMSUN.TTC");
|
||||
InputStream inputStream = new ClassPathResourceProvider().doGetResourceInputStream(fontResource);
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
|
||||
font = font.deriveFont(Font.BOLD, 70);
|
||||
float currentFontTopCoef = 0.14645833f * font.getSize() + 0.39583333f;
|
||||
fonts.add(new FontWrapper(font, currentFontTopCoef));
|
||||
throw new ImageCaptchaException("初始化文字点选验证码失败,请设置字体包后再调用init()");
|
||||
}
|
||||
if (initDefaultResource) {
|
||||
initDefaultResource();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void initDefaultResource() {
|
||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||
// 添加一些系统的资源文件
|
||||
resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"), DEFAULT_TAG));
|
||||
}
|
||||
|
||||
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
|
||||
import static cloud.tianai.captcha.generator.impl.StandardSliderImageCaptchaGenerator.TEMPLATE_ACTIVE_IMAGE_NAME;
|
||||
import static cloud.tianai.captcha.generator.impl.StandardSliderImageCaptchaGenerator.TEMPLATE_FIXED_IMAGE_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2024/7/15 9:10
|
||||
* @Description 默认资源配置
|
||||
* 注意: 不推荐使用该类,应该将资源模板自己设置,而不是使用默认的,这里编写的目的只是为了演示方便
|
||||
*/
|
||||
public class DefaultBuiltInResources {
|
||||
|
||||
public static final String PATH_PREFIX = "classpath:META-INF/cut-image/template";
|
||||
|
||||
private static Map<String, Consumer<ResourceStore>> defaultTemplateResource = new HashMap<>(8);
|
||||
|
||||
|
||||
public DefaultBuiltInResources(String defaultPathPrefix) {
|
||||
init(defaultPathPrefix);
|
||||
}
|
||||
|
||||
private void init(String defaultPathPrefix) {
|
||||
String[] split = defaultPathPrefix.split(":");
|
||||
String type;
|
||||
String pathPrefix;
|
||||
if (split.length < 1) {
|
||||
type = "file";
|
||||
pathPrefix = defaultPathPrefix;
|
||||
} else {
|
||||
type = split[0];
|
||||
pathPrefix = split[1];
|
||||
}
|
||||
if (pathPrefix.endsWith("/")) {
|
||||
pathPrefix = pathPrefix.substring(0, pathPrefix.length() - 1);
|
||||
}
|
||||
// 滑动验证
|
||||
String finalPathPrefix = pathPrefix;
|
||||
defaultTemplateResource.put(CaptchaTypeConstant.SLIDER, resourceStore -> {
|
||||
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(type, finalPathPrefix.concat("/slider_1/active.png")));
|
||||
template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(type, finalPathPrefix.concat("/slider_1/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template1);
|
||||
|
||||
ResourceMap template2 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template2.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(type, finalPathPrefix.concat("/slider_2/active.png")));
|
||||
template2.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(type, finalPathPrefix.concat("/slider_2/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template2);
|
||||
});
|
||||
|
||||
// 旋转验证
|
||||
defaultTemplateResource.put(CaptchaTypeConstant.ROTATE, resourceStore -> {
|
||||
// 添加一些系统的 模板文件
|
||||
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
|
||||
template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(type, finalPathPrefix.concat("/rotate_1/active.png")));
|
||||
template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource(type, finalPathPrefix.concat("/rotate_1/fixed.png")));
|
||||
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void addDefaultTemplate(String type, ResourceStore resourceStore) {
|
||||
Consumer<ResourceStore> resourceStoreConsumer = defaultTemplateResource.get(type);
|
||||
if (resourceStoreConsumer == null) {
|
||||
return;
|
||||
}
|
||||
resourceStoreConsumer.accept(resourceStore);
|
||||
}
|
||||
|
||||
public void addDefaultTemplate(ResourceStore resourceStore) {
|
||||
defaultTemplateResource.forEach((type, consumer) -> {
|
||||
consumer.accept(resourceStore);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
@@ -54,7 +54,7 @@ public class ApplicationTest {
|
||||
// 验证码资源管理器 该类负责管理验证码背景图和模板图等数据
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
// 验证码生成器; 注意: 生成器必须调用init(...)初始化方法 true为加载默认资源,false为不加载,
|
||||
ImageCaptchaGenerator generator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager).init(true);
|
||||
ImageCaptchaGenerator generator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager).init();
|
||||
// 验证码校验器
|
||||
ImageCaptchaValidator imageCaptchaValidator = new SimpleImageCaptchaValidator();
|
||||
// 缓存, 用于存放校验数据
|
||||
|
||||
@@ -22,7 +22,7 @@ public class SimpleDemo {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
ImageTransform imageTransform = new Base64ImageTransform();
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform).init(true);
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform).init();
|
||||
BasicCaptchaTrackValidator imageCaptchaValidator = new BasicCaptchaTrackValidator();
|
||||
// 注意: 上面这个四个对象都是单例的, 整个项目创建一次即可
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package example.readme;
|
||||
|
||||
import cloud.tianai.captcha.application.ImageCaptchaApplication;
|
||||
import cloud.tianai.captcha.application.TACBuilder;
|
||||
import cloud.tianai.captcha.application.vo.CaptchaResponse;
|
||||
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
|
||||
public class TACBuilderTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
TACBuilder builder = TACBuilder.builder();
|
||||
ImageCaptchaApplication application = builder
|
||||
.addDefaultTemplate()
|
||||
.addResource("SLIDER", new Resource("classpath", "META-INF/cut-image/resource/1.jpg"))
|
||||
.build();
|
||||
CaptchaResponse<ImageCaptchaVO> response = application.generateCaptcha("SLIDER");
|
||||
System.out.println(response);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class Test {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
Base64ImageTransform imageTransform = new Base64ImageTransform();
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform).init(true);
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform).init();
|
||||
/*
|
||||
生成滑块验证码图片, 可选项
|
||||
SLIDER (滑块验证码)
|
||||
|
||||
@@ -15,7 +15,7 @@ public class Test3 {
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
Base64ImageTransform imageTransform = new Base64ImageTransform();
|
||||
// 标准验证码生成器
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform).init(true);
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,imageTransform).init();
|
||||
// 生成 具有混淆的 滑块验证码 (目前只有滑块验证码支持混淆滑块, 旋转验证,滑动还原,点选验证 均不支持混淆功能)
|
||||
ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(GenerateParam.builder()
|
||||
// 设置验证码类型
|
||||
|
||||
@@ -14,7 +14,7 @@ public class Test4 {
|
||||
// 资源管理器
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
// 标准验证码生成器
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,new Base64ImageTransform()).init(true);
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,new Base64ImageTransform()).init();
|
||||
// 生成旋转验证码 图片类型为 webp
|
||||
// 注意 tianai-captcha 后面默认删除了生成webp格式图片需要用户自定义添加webp转换的工具,需要用户自定义添加和扩展
|
||||
// 参考 https://bitbucket.org/luciad/webp-imageio
|
||||
|
||||
@@ -30,7 +30,7 @@ public class Test7 {
|
||||
}
|
||||
};
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,new Base64ImageTransform()).init(false);
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager,new Base64ImageTransform()).init();
|
||||
// 注册
|
||||
imageCaptchaResourceManager.registerResourceProvider(resourceProvider);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class Test8 {
|
||||
// 参数四: 检查时间间隔
|
||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||
ImageCaptchaGenerator imageCaptchaGenerator = new CacheImageCaptchaGenerator(new MultiImageCaptchaGenerator(imageCaptchaResourceManager,new Base64ImageTransform()), 10, 1000, 100);
|
||||
imageCaptchaGenerator.init(true);
|
||||
imageCaptchaGenerator.init();
|
||||
// 生成滑块图片
|
||||
ImageCaptchaInfo slideImageInfo = imageCaptchaGenerator.generateCaptchaImage(CaptchaTypeConstant.SLIDER);
|
||||
// 获取背景图片的base64
|
||||
|
||||