1.5.0正式版

This commit is contained in:
天爱有情
2024-07-15 13:16:56 +08:00
parent 2782ef6d66
commit 0a1ec7adda
29 changed files with 350 additions and 161 deletions
+1 -1
View File
@@ -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));
}
if (initDefaultResource) {
initDefaultResource();
}
throw new ImageCaptchaException("初始化文字点选验证码失败,请设置字体包后再调用init()");
}
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);
}
}
+1 -1
View File
@@ -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 (滑块验证码)
+1 -1
View File
@@ -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()
// 设置验证码类型
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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