diff --git a/pom.xml b/pom.xml index 9457263..b617096 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 cloud.tianai.captcha tianai-captcha - 1.5.0.beta + 1.5.0 tianai-captcha 行为验证码 diff --git a/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java b/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java index bc9af2f..bf31170 100644 --- a/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java +++ b/src/main/java/cloud/tianai/captcha/application/DefaultImageCaptchaApplication.java @@ -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 diff --git a/src/main/java/cloud/tianai/captcha/application/ImageCaptchaProperties.java b/src/main/java/cloud/tianai/captcha/application/ImageCaptchaProperties.java index 654d098..6af6aff 100644 --- a/src/main/java/cloud/tianai/captcha/application/ImageCaptchaProperties.java +++ b/src/main/java/cloud/tianai/captcha/application/ImageCaptchaProperties.java @@ -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 expire = Collections.emptyMap(); + private Map expire = new HashMap<>(); + + // 本地提前缓存 + private boolean localCacheEnabled = false; + private int localCacheSize = 10; + private int localCacheWaitTime = 1000; + private int localCachePeriod = 5000; + private Long localCacheExpireTime; } diff --git a/src/main/java/cloud/tianai/captcha/application/TACBuilder.java b/src/main/java/cloud/tianai/captcha/application/TACBuilder.java new file mode 100644 index 0000000..dbda9a0 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/application/TACBuilder.java @@ -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 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; + } +} diff --git a/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java index 87e0867..292351e 100644 --- a/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/AbstractImageCaptchaGenerator.java @@ -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(); /** * 生成验证码方法 diff --git a/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaGenerator.java index 25bf2eb..12408a6 100644 --- a/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaGenerator.java @@ -17,10 +17,9 @@ public interface ImageCaptchaGenerator { /** * 初始化 * - * @param initDefaultResource 是否初始化默认资源 * @return ImageCaptchaGenerator */ - ImageCaptchaGenerator init(boolean initDefaultResource); + ImageCaptchaGenerator init(); /** * 生成验证码图片 diff --git a/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaPostProcessor.java b/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaPostProcessor.java deleted file mode 100644 index 58a744c..0000000 --- a/src/main/java/cloud/tianai/captcha/generator/ImageCaptchaPostProcessor.java +++ /dev/null @@ -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) { - - } -} diff --git a/src/main/java/cloud/tianai/captcha/generator/common/FontWrapper.java b/src/main/java/cloud/tianai/captcha/generator/common/FontWrapper.java index 7f4f59c..b09c311 100644 --- a/src/main/java/cloud/tianai/captcha/generator/common/FontWrapper.java +++ b/src/main/java/cloud/tianai/captcha/generator/common/FontWrapper.java @@ -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; + } } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/CacheImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/CacheImageCaptchaGenerator.java index 90f26a4..1194ea4 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/CacheImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/CacheImageCaptchaGenerator.java @@ -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; diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java index 6af1c8a..c3e992c 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/MultiImageCaptchaGenerator.java @@ -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 imageCaptchaGeneratorMap = new ConcurrentHashMap<>(4); protected Map imageCaptchaGeneratorProviderMap = new HashMap<>(4); - + // 点选类验证码字体 + @Setter + @Getter + protected List 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; } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java index 99ade97..9c6f2ef 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardConcatImageCaptchaGenerator.java @@ -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) { diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java index f7a347c..a5dd5a8 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardRotateImageCaptchaGenerator.java @@ -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) { diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java index f3003d9..b61ee70 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardSliderImageCaptchaGenerator.java @@ -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); - } } diff --git a/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java index 6f08522..9cbc881 100644 --- a/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java @@ -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 imageCheckDefinitions) { diff --git a/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java b/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java new file mode 100644 index 0000000..7baa330 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/resource/DefaultBuiltInResources.java @@ -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> 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 resourceStoreConsumer = defaultTemplateResource.get(type); + if (resourceStoreConsumer == null) { + return; + } + resourceStoreConsumer.accept(resourceStore); + } + + public void addDefaultTemplate(ResourceStore resourceStore) { + defaultTemplateResource.forEach((type, consumer) -> { + consumer.accept(resourceStore); + }); + } + +} diff --git a/src/main/resources/META-INF/cut-image/template/3/active.png b/src/main/resources/META-INF/cut-image/template/rotate_1/active.png similarity index 100% rename from src/main/resources/META-INF/cut-image/template/3/active.png rename to src/main/resources/META-INF/cut-image/template/rotate_1/active.png diff --git a/src/main/resources/META-INF/cut-image/template/3/fixed.png b/src/main/resources/META-INF/cut-image/template/rotate_1/fixed.png similarity index 100% rename from src/main/resources/META-INF/cut-image/template/3/fixed.png rename to src/main/resources/META-INF/cut-image/template/rotate_1/fixed.png diff --git a/src/main/resources/META-INF/cut-image/template/1/active.png b/src/main/resources/META-INF/cut-image/template/slider_1/active.png similarity index 100% rename from src/main/resources/META-INF/cut-image/template/1/active.png rename to src/main/resources/META-INF/cut-image/template/slider_1/active.png diff --git a/src/main/resources/META-INF/cut-image/template/1/fixed.png b/src/main/resources/META-INF/cut-image/template/slider_1/fixed.png similarity index 100% rename from src/main/resources/META-INF/cut-image/template/1/fixed.png rename to src/main/resources/META-INF/cut-image/template/slider_1/fixed.png diff --git a/src/main/resources/META-INF/cut-image/template/2/active.png b/src/main/resources/META-INF/cut-image/template/slider_2/active.png similarity index 100% rename from src/main/resources/META-INF/cut-image/template/2/active.png rename to src/main/resources/META-INF/cut-image/template/slider_2/active.png diff --git a/src/main/resources/META-INF/cut-image/template/2/fixed.png b/src/main/resources/META-INF/cut-image/template/slider_2/fixed.png similarity index 100% rename from src/main/resources/META-INF/cut-image/template/2/fixed.png rename to src/main/resources/META-INF/cut-image/template/slider_2/fixed.png diff --git a/src/main/test/java/example/readme/ApplicationTest.java b/src/main/test/java/example/readme/ApplicationTest.java index 5dca1da..bb812ed 100644 --- a/src/main/test/java/example/readme/ApplicationTest.java +++ b/src/main/test/java/example/readme/ApplicationTest.java @@ -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(); // 缓存, 用于存放校验数据 diff --git a/src/main/test/java/example/readme/SimpleDemo.java b/src/main/test/java/example/readme/SimpleDemo.java index 2359619..297349d 100644 --- a/src/main/test/java/example/readme/SimpleDemo.java +++ b/src/main/test/java/example/readme/SimpleDemo.java @@ -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(); // 注意: 上面这个四个对象都是单例的, 整个项目创建一次即可 diff --git a/src/main/test/java/example/readme/TACBuilderTest.java b/src/main/test/java/example/readme/TACBuilderTest.java new file mode 100644 index 0000000..1b15595 --- /dev/null +++ b/src/main/test/java/example/readme/TACBuilderTest.java @@ -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 response = application.generateCaptcha("SLIDER"); + System.out.println(response); + + } +} + diff --git a/src/main/test/java/example/readme/Test.java b/src/main/test/java/example/readme/Test.java index e68d956..5a44ecf 100644 --- a/src/main/test/java/example/readme/Test.java +++ b/src/main/test/java/example/readme/Test.java @@ -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 (滑块验证码) diff --git a/src/main/test/java/example/readme/Test3.java b/src/main/test/java/example/readme/Test3.java index 0c0575a..2646145 100644 --- a/src/main/test/java/example/readme/Test3.java +++ b/src/main/test/java/example/readme/Test3.java @@ -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() // 设置验证码类型 diff --git a/src/main/test/java/example/readme/Test4.java b/src/main/test/java/example/readme/Test4.java index 752e6d1..fcd27a1 100644 --- a/src/main/test/java/example/readme/Test4.java +++ b/src/main/test/java/example/readme/Test4.java @@ -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 diff --git a/src/main/test/java/example/readme/Test7.java b/src/main/test/java/example/readme/Test7.java index 57f9b62..2f9505c 100644 --- a/src/main/test/java/example/readme/Test7.java +++ b/src/main/test/java/example/readme/Test7.java @@ -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); } diff --git a/src/main/test/java/example/readme/Test8.java b/src/main/test/java/example/readme/Test8.java index 0e3aed0..22733f7 100644 --- a/src/main/test/java/example/readme/Test8.java +++ b/src/main/test/java/example/readme/Test8.java @@ -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