mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
feat(resource): 重构资源存储和加载机制
- 新增 AbstractResourceStore 类,实现 ResourceStore 接口的通用逻辑 - 创建 FontCache 类,用于缓存和管理字体资源 - 重构 DefaultImageCaptchaResourceManager 类,支持资源提供者和监听器 - 更新 Resource 和 ResourceMap 类,增加唯一 ID 字段 - 新增 ResourceListener 接口,用于扩展资源存储功能 - 创建 ResourceProviders 类,统一管理资源提供者 - 更新 TACBuilder 类,支持新的资源存储和加载机制
This commit is contained in:
@@ -2,29 +2,22 @@ package cloud.tianai.captcha.application;
|
|||||||
|
|
||||||
import cloud.tianai.captcha.cache.CacheStore;
|
import cloud.tianai.captcha.cache.CacheStore;
|
||||||
import cloud.tianai.captcha.cache.impl.LocalCacheStore;
|
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.ImageCaptchaGenerator;
|
||||||
import cloud.tianai.captcha.generator.ImageTransform;
|
import cloud.tianai.captcha.generator.ImageTransform;
|
||||||
import cloud.tianai.captcha.generator.common.FontWrapper;
|
|
||||||
import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
|
import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
|
||||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||||
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
||||||
import cloud.tianai.captcha.resource.DefaultBuiltInResources;
|
import cloud.tianai.captcha.resource.DefaultBuiltInResources;
|
||||||
|
import cloud.tianai.captcha.resource.FontCache;
|
||||||
|
import cloud.tianai.captcha.resource.ResourceProviders;
|
||||||
import cloud.tianai.captcha.resource.ResourceStore;
|
import cloud.tianai.captcha.resource.ResourceStore;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
|
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
|
||||||
import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore;
|
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.ImageCaptchaValidator;
|
||||||
import cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator;
|
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: 天爱有情
|
* @Author: 天爱有情
|
||||||
* @date 2024/7/14 16:41
|
* @date 2024/7/14 16:41
|
||||||
@@ -39,18 +32,20 @@ public class TACBuilder {
|
|||||||
private ImageCaptchaProperties prop = new ImageCaptchaProperties();
|
private ImageCaptchaProperties prop = new ImageCaptchaProperties();
|
||||||
private ResourceStore resourceStore;
|
private ResourceStore resourceStore;
|
||||||
private ImageTransform imageTransform;
|
private ImageTransform imageTransform;
|
||||||
private List<FontWrapper> fontWrappers = new ArrayList<>();
|
// private List<FontWrapper> fontWrappers = new ArrayList<>();
|
||||||
|
|
||||||
public static TACBuilder builder() {
|
public static TACBuilder builder() {
|
||||||
TACBuilder builder = new TACBuilder();
|
return TACBuilder.builder(new LocalMemoryResourceStore());
|
||||||
// 默认设置本地的
|
}
|
||||||
LocalMemoryResourceStore resourceStore = new LocalMemoryResourceStore();
|
|
||||||
builder.resourceStore = resourceStore;
|
public static TACBuilder builder(ResourceStore resourceStore) {
|
||||||
|
TACBuilder builder = new TACBuilder(resourceStore);
|
||||||
builder.prop = new ImageCaptchaProperties();
|
builder.prop = new ImageCaptchaProperties();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TACBuilder() {
|
private TACBuilder(ResourceStore resourceStore) {
|
||||||
|
this.resourceStore = resourceStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
public TACBuilder addDefaultTemplate(String defaultPathPrefix) {
|
||||||
@@ -83,15 +78,11 @@ public class TACBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder addFont(FontWrapper fontWrapper) {
|
public TACBuilder addFont(Resource resource) {
|
||||||
this.fontWrappers.add(fontWrapper);
|
this.addResource(FontCache.FONT_TYPE, resource);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder addFont(Font font) {
|
|
||||||
return addFont(new FontWrapper(font));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TACBuilder cached(int size, int waitTime, int period, Long expireTime) {
|
public TACBuilder cached(int size, int waitTime, int period, Long expireTime) {
|
||||||
prop.setLocalCacheEnabled(true);
|
prop.setLocalCacheEnabled(true);
|
||||||
@@ -117,10 +108,10 @@ public class TACBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TACBuilder setResourceStore(ResourceStore resourceStore) {
|
// public TACBuilder setResourceStore(ResourceStore resourceStore) {
|
||||||
this.resourceStore = resourceStore;
|
// this.resourceStore = resourceStore;
|
||||||
return this;
|
// return this;
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
public TACBuilder addResource(String captchaType, Resource imageResource) {
|
public TACBuilder addResource(String captchaType, Resource imageResource) {
|
||||||
@@ -143,31 +134,19 @@ public class TACBuilder {
|
|||||||
cacheStore = new LocalCacheStore();
|
cacheStore = new LocalCacheStore();
|
||||||
}
|
}
|
||||||
if (generator == null) {
|
if (generator == null) {
|
||||||
DefaultImageCaptchaResourceManager resourceManager = new DefaultImageCaptchaResourceManager(resourceStore);
|
ResourceProviders resourceProviders = new ResourceProviders();
|
||||||
|
DefaultImageCaptchaResourceManager resourceManager = new DefaultImageCaptchaResourceManager(resourceStore, resourceProviders);
|
||||||
generator = new MultiImageCaptchaGenerator(resourceManager, imageTransform);
|
generator = new MultiImageCaptchaGenerator(resourceManager, imageTransform);
|
||||||
}
|
}
|
||||||
if (generator instanceof MultiImageCaptchaGenerator) {
|
// if (generator instanceof MultiImageCaptchaGenerator) {
|
||||||
if (CollectionUtils.isEmpty(fontWrappers)) {
|
// ((MultiImageCaptchaGenerator) generator).setFontWrappers(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) {
|
if (validator == null) {
|
||||||
validator = new SimpleImageCaptchaValidator();
|
validator = new SimpleImageCaptchaValidator();
|
||||||
}
|
}
|
||||||
if (interceptor == null) {
|
if (interceptor == null) {
|
||||||
interceptor = EmptyCaptchaInterceptor.INSTANCE;
|
interceptor = EmptyCaptchaInterceptor.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultImageCaptchaApplication application = new DefaultImageCaptchaApplication(generator, validator, cacheStore, prop, interceptor);
|
DefaultImageCaptchaApplication application = new DefaultImageCaptchaApplication(generator, validator, cacheStore, prop, interceptor);
|
||||||
return application;
|
return application;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package cloud.tianai.captcha.common.util;
|
||||||
|
|
||||||
|
public class UUIDUtils {
|
||||||
|
|
||||||
|
public static String getUUID() {
|
||||||
|
return java.util.UUID.randomUUID().toString().replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -62,4 +62,23 @@ public class GenerateParam {
|
|||||||
return param.getOrDefault(key, defaultValue);
|
return param.getOrDefault(key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object putIfAbsent(String key, Object value) {
|
||||||
|
return doGetOrCreateParam().putIfAbsent(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T> void addParam(ParamKey<T> paramKey, T value) {
|
||||||
|
addParam(paramKey.getKey(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getParam(ParamKey<T> paramKey) {
|
||||||
|
return (T) getParam(paramKey.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getOrDefault(ParamKey<T> paramKey, T defaultValue) {
|
||||||
|
return (T) getOrDefault(paramKey.getKey(), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package cloud.tianai.captcha.generator.common.model.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2024/11/20 11:34
|
||||||
|
* @Description 此接口的作用是在给 {@link GenerateParam} 添加/获取参数时做一个类型限制和转换
|
||||||
|
*/
|
||||||
|
public interface ParamKey<T> {
|
||||||
|
|
||||||
|
String getKey();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
package cloud.tianai.captcha.resource;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class AbstractResourceStore implements ResourceStore {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected ChainListener listener = new ChainListener();
|
||||||
|
|
||||||
|
boolean isInit = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(ImageCaptchaResourceManager resourceManager) {
|
||||||
|
if (isInit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doInit();
|
||||||
|
isInit = true;
|
||||||
|
listener.onInit(this, resourceManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(ResourceListener listener) {
|
||||||
|
this.listener.removeListener(listener);
|
||||||
|
this.listener.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResource(String type, Resource resource) {
|
||||||
|
doAddResource(type, resource);
|
||||||
|
if (isInit) {
|
||||||
|
listener.onAddResource(type, resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTemplate(String type, ResourceMap template) {
|
||||||
|
doAddTemplate(type, template);
|
||||||
|
if (isInit) {
|
||||||
|
listener.onAddTemplate(type, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource deleteResource(String type, String id) {
|
||||||
|
Resource resource = doDeleteResource(type, id);
|
||||||
|
if (isInit && resource != null) {
|
||||||
|
listener.onDeleteResource(type, resource);
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceMap deleteTemplate(String type, String id) {
|
||||||
|
ResourceMap resourceMap = doDeleteTemplate(type, id);
|
||||||
|
if (isInit && resourceMap != null) {
|
||||||
|
listener.onDeleteTemplate(type, resourceMap);
|
||||||
|
}
|
||||||
|
return resourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource randomGetResourceByTypeAndTag(String type, String tag) {
|
||||||
|
Resource resource = doRandomGetResourceByTypeAndTag(type, tag);
|
||||||
|
if (isInit && resource != null) {
|
||||||
|
listener.onRandomGetResourceByTypeAndTag(type, tag, resource);
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) {
|
||||||
|
ResourceMap resourceMap = doRandomGetTemplateByTypeAndTag(type, tag);
|
||||||
|
if (isInit && resourceMap != null) {
|
||||||
|
listener.onRandomGetTemplateByTypeAndTag(type, tag, resourceMap);
|
||||||
|
}
|
||||||
|
return resourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearAllResources() {
|
||||||
|
doClearAllResources();
|
||||||
|
if (isInit) {
|
||||||
|
listener.onClearAllResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearAllTemplates() {
|
||||||
|
doClearAllTemplates();
|
||||||
|
if (isInit) {
|
||||||
|
listener.onClearAllTemplates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doInit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void doClearAllResources();
|
||||||
|
|
||||||
|
public abstract void doClearAllTemplates();
|
||||||
|
|
||||||
|
public abstract Resource doRandomGetResourceByTypeAndTag(String type, String tag);
|
||||||
|
|
||||||
|
public abstract ResourceMap doRandomGetTemplateByTypeAndTag(String type, String tag);
|
||||||
|
|
||||||
|
public abstract ResourceMap doDeleteTemplate(String type, String id);
|
||||||
|
|
||||||
|
public abstract Resource doDeleteResource(String type, String id);
|
||||||
|
|
||||||
|
public abstract void doAddResource(String type, Resource resource);
|
||||||
|
|
||||||
|
public abstract void doAddTemplate(String type, ResourceMap template);
|
||||||
|
|
||||||
|
|
||||||
|
public static class ChainListener implements ResourceListener {
|
||||||
|
protected List<ResourceListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
public ChainListener() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(ResourceListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(ResourceListener listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onInit(resourceStore, resourceManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddResource(String type, Resource resource) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onAddResource(type, resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddTemplate(String type, ResourceMap template) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onAddTemplate(type, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClearAllResources() {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onClearAllResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClearAllTemplates() {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onClearAllTemplates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRandomGetResourceByTypeAndTag(String type, String tag, Resource resource) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onRandomGetResourceByTypeAndTag(type, tag, resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRandomGetTemplateByTypeAndTag(String type, String tag, ResourceMap template) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onRandomGetTemplateByTypeAndTag(type, tag, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteResource(String type, Resource resource) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onDeleteResource(type, resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteTemplate(String type, ResourceMap template) {
|
||||||
|
for (ResourceListener listener : listeners) {
|
||||||
|
listener.onDeleteTemplate(type, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package cloud.tianai.captcha.resource;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.generator.common.FontWrapper;
|
||||||
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2024/11/19 11:25
|
||||||
|
* @Description 一个用于统一缓存字体文件的对象
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FontCache implements ResourceListener {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String FONT_TYPE = "font";
|
||||||
|
private final Map<String, FontWrapper> fontMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private ResourceStore resourceStore;
|
||||||
|
private ImageCaptchaResourceManager resourceManager;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private int fontSize = 70;
|
||||||
|
public static FontCache getInstance() {
|
||||||
|
return INSTANCE.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontCache() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
|
||||||
|
this.resourceStore = resourceStore;
|
||||||
|
this.resourceManager = resourceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontWrapper getFont(Resource resource) {
|
||||||
|
try (InputStream stream = resourceManager.getResourceInputStream(resource)) {
|
||||||
|
Font font = Font.createFont(0, stream);
|
||||||
|
return new FontWrapper(font, fontSize);
|
||||||
|
} catch (FontFormatException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddResource(String type, Resource resource) {
|
||||||
|
if (FONT_TYPE.equalsIgnoreCase(type)) {
|
||||||
|
fontMap.computeIfAbsent(resource.getId(), v -> getFont(resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteResource(String type, Resource resource) {
|
||||||
|
if (FONT_TYPE.equalsIgnoreCase(type)) {
|
||||||
|
fontMap.remove(resource.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClearAllResources() {
|
||||||
|
fontMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRandomGetResourceByTypeAndTag(String type, String tag, Resource resource) {
|
||||||
|
if (FONT_TYPE.equalsIgnoreCase(type)) {
|
||||||
|
FontWrapper fontWrapper = fontMap.computeIfAbsent(resource.getId(), v -> getFont(resource));
|
||||||
|
|
||||||
|
resource.setExtra(fontWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadAllFonts() {
|
||||||
|
List<Resource> resources = resourceStore.listResourcesByTypeAndTag(FONT_TYPE, null);
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
fontMap.computeIfAbsent(resource.getId(), v -> getFont(resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class INSTANCE {
|
||||||
|
private static final FontCache INSTANCE = new FontCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package cloud.tianai.captcha.resource;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2024/11/19 9:26
|
||||||
|
* @Description 此类负责对 ResourceStore 进行一下扩展增强, 对ResourceStore的相关方法添加一个hook回调
|
||||||
|
*/
|
||||||
|
public interface ResourceListener {
|
||||||
|
|
||||||
|
default void onInit(ResourceStore resourceStore, ImageCaptchaResourceManager resourceManager) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default void onAddResource(String type, Resource resource) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onAddTemplate(String type, ResourceMap template) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onDeleteResource(String type, Resource resource) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onDeleteTemplate(String type, ResourceMap template) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onClearAllResources() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onClearAllTemplates() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onRandomGetResourceByTypeAndTag(String type, String tag, Resource resource) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onRandomGetTemplateByTypeAndTag(String type, String tag, ResourceMap template) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,10 +22,10 @@ public interface ResourceProvider {
|
|||||||
/**
|
/**
|
||||||
* 是否支持
|
* 是否支持
|
||||||
*
|
*
|
||||||
* @param type type
|
* @param resource resource
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
boolean supported(String type);
|
boolean supported(Resource resource);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 放弃资源提供者名称
|
* 放弃资源提供者名称
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package cloud.tianai.captcha.resource;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
|
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
||||||
|
import cloud.tianai.captcha.resource.impl.provider.FileResourceProvider;
|
||||||
|
import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ResourceProviders {
|
||||||
|
|
||||||
|
private final List<ResourceProvider> resourceProviderList = new ArrayList<>(8);
|
||||||
|
|
||||||
|
|
||||||
|
public ResourceProviders() {
|
||||||
|
registerResourceProvider(new URLResourceProvider());
|
||||||
|
registerResourceProvider(new ClassPathResourceProvider());
|
||||||
|
registerResourceProvider(new FileResourceProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerResourceProvider(ResourceProvider resourceProvider) {
|
||||||
|
deleteResourceProviderByName(resourceProvider.getName());
|
||||||
|
resourceProviderList.add(resourceProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteResourceProviderByName(String name) {
|
||||||
|
return resourceProviderList.removeIf(r -> r.getName().equals(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ResourceProvider> listResourceProviders() {
|
||||||
|
return Collections.unmodifiableList(resourceProviderList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public InputStream getResourceInputStream(Resource resource) {
|
||||||
|
for (ResourceProvider resourceProvider : resourceProviderList) {
|
||||||
|
if (resourceProvider.supported(resource)) {
|
||||||
|
InputStream resourceInputStream = resourceProvider.getResourceInputStream(resource);
|
||||||
|
if (resourceInputStream == null) {
|
||||||
|
throw new IllegalArgumentException("滑块验证码 ResourceProvider 读到的图片资源为空,providerName=["
|
||||||
|
+ resourceProvider.getName() + "], resource=[" + resource + "]");
|
||||||
|
}
|
||||||
|
return resourceInputStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("没有找到Resource [" + resource.getType() + "]对应的资源提供者");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package cloud.tianai.captcha.resource;
|
|||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: 天爱有情
|
* @Author: 天爱有情
|
||||||
* @date 2022/5/7 9:04
|
* @date 2022/5/7 9:04
|
||||||
@@ -10,6 +12,14 @@ import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
|||||||
*/
|
*/
|
||||||
public interface ResourceStore {
|
public interface ResourceStore {
|
||||||
|
|
||||||
|
void init(ImageCaptchaResourceManager resourceManager);
|
||||||
|
/**
|
||||||
|
* 给ResourceStore添加hook,用于一些扩展
|
||||||
|
*
|
||||||
|
* @param hook
|
||||||
|
*/
|
||||||
|
void addListener(ResourceListener hook);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加资源
|
* 添加资源
|
||||||
*
|
*
|
||||||
@@ -27,6 +37,42 @@ public interface ResourceStore {
|
|||||||
*/
|
*/
|
||||||
void addTemplate(String type, ResourceMap template);
|
void addTemplate(String type, ResourceMap template);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除资源
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param id 资源ID
|
||||||
|
* @return Resource
|
||||||
|
*/
|
||||||
|
Resource deleteResource(String type, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除模板
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param id 资源ID
|
||||||
|
* @return ResourceMap
|
||||||
|
*/
|
||||||
|
ResourceMap deleteTemplate(String type, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某个资源列表
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param tag 资源标签(可为空)
|
||||||
|
* @return List<Resource>
|
||||||
|
*/
|
||||||
|
List<Resource> listResourcesByTypeAndTag(String type, String tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某个模板列表
|
||||||
|
*
|
||||||
|
* @param type 验证码类型
|
||||||
|
* @param tag 资源标签(可为空)
|
||||||
|
* @return List<ResourceMap>
|
||||||
|
*/
|
||||||
|
List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 随机获取某个资源
|
* 随机获取某个资源
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cloud.tianai.captcha.resource.common.model.dto;
|
package cloud.tianai.captcha.resource.common.model.dto;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.common.util.UUIDUtils;
|
||||||
import cloud.tianai.captcha.resource.ResourceProvider;
|
import cloud.tianai.captcha.resource.ResourceProvider;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -12,31 +13,37 @@ import lombok.NoArgsConstructor;
|
|||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class Resource {
|
public class Resource {
|
||||||
|
/** 唯一ID. */
|
||||||
|
private String id;
|
||||||
/** 类型. */
|
/** 类型. */
|
||||||
private String type;
|
private String type;
|
||||||
/** 数据,传输给 {@link ResourceProvider} 的参数 */
|
/** 数据,传输给 {@link ResourceProvider} 的参数 */
|
||||||
public String data;
|
public String data;
|
||||||
/** 标签.*/
|
/** 标签. */
|
||||||
private String tag;
|
private String tag;
|
||||||
/** 提示.*/
|
/** 提示. */
|
||||||
private String tip;
|
private String tip;
|
||||||
/** 扩展.*/
|
/** 扩展. */
|
||||||
private Object extra;
|
private Object extra;
|
||||||
|
|
||||||
public Resource(String type, String data) {
|
public Resource(String type, String data) {
|
||||||
this.type = type;
|
this(type, data, null);
|
||||||
this.data = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource(String type, String data, String tag) {
|
public Resource(String type, String data, String tag) {
|
||||||
this.type = type;
|
this(type, data, tag, null);
|
||||||
this.data = data;
|
|
||||||
this.tag = tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource(String type, String data, String tag, String tip) {
|
public Resource(String type, String data, String tag, String tip) {
|
||||||
|
this(UUIDUtils.getUUID(), type, data, tag, tip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource(String id, String type, String data, String tag, String tip) {
|
||||||
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.tip = tip;
|
this.tip = tip;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cloud.tianai.captcha.resource.common.model.dto;
|
package cloud.tianai.captcha.resource.common.model.dto;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.common.util.UUIDUtils;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
@@ -16,25 +17,31 @@ import java.util.function.BiConsumer;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class ResourceMap {
|
public class ResourceMap {
|
||||||
|
/** 唯一ID. */
|
||||||
|
private String id;
|
||||||
private Map<String, Resource> resourceMap;
|
private Map<String, Resource> resourceMap;
|
||||||
private String tag;
|
private String tag;
|
||||||
|
|
||||||
public ResourceMap(String tag) {
|
public ResourceMap(String tag) {
|
||||||
this.tag = tag;
|
this(tag, 10);
|
||||||
this.resourceMap = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceMap(String tag, int initialCapacity) {
|
public ResourceMap(String tag, int initialCapacity) {
|
||||||
|
this(UUIDUtils.getUUID(), tag, initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceMap(String id, String tag, int initialCapacity) {
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.resourceMap = new HashMap<>(initialCapacity);
|
this.resourceMap = new HashMap<>(initialCapacity);
|
||||||
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceMap(int initialCapacity) {
|
public ResourceMap(int initialCapacity) {
|
||||||
this.resourceMap = new HashMap<>(initialCapacity);
|
this(null, initialCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceMap() {
|
public ResourceMap() {
|
||||||
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Resource> getResourceMapOfCreate() {
|
private Map<String, Resource> getResourceMapOfCreate() {
|
||||||
|
|||||||
+13
-30
@@ -1,17 +1,11 @@
|
|||||||
package cloud.tianai.captcha.resource.impl;
|
package cloud.tianai.captcha.resource.impl;
|
||||||
|
|
||||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
import cloud.tianai.captcha.resource.*;
|
||||||
import cloud.tianai.captcha.resource.ResourceProvider;
|
|
||||||
import cloud.tianai.captcha.resource.ResourceStore;
|
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
import lombok.Getter;
|
||||||
import cloud.tianai.captcha.resource.impl.provider.FileResourceProvider;
|
|
||||||
import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,15 +18,16 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
|
|||||||
/** 资源存储. */
|
/** 资源存储. */
|
||||||
private ResourceStore resourceStore;
|
private ResourceStore resourceStore;
|
||||||
/** 资源转换 转换为stream流. */
|
/** 资源转换 转换为stream流. */
|
||||||
private final List<ResourceProvider> resourceProviderList = new ArrayList<>(8);
|
@Getter
|
||||||
|
private ResourceProviders resourceProviders;
|
||||||
|
|
||||||
public DefaultImageCaptchaResourceManager() {
|
public DefaultImageCaptchaResourceManager() {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultImageCaptchaResourceManager(ResourceStore resourceStore) {
|
public DefaultImageCaptchaResourceManager(ResourceStore resourceStore, ResourceProviders resourceProviders) {
|
||||||
this.resourceStore = resourceStore;
|
this.resourceStore = resourceStore;
|
||||||
|
this.resourceProviders = resourceProviders;
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,10 +35,9 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
|
|||||||
if (this.resourceStore == null) {
|
if (this.resourceStore == null) {
|
||||||
this.resourceStore = new LocalMemoryResourceStore();
|
this.resourceStore = new LocalMemoryResourceStore();
|
||||||
}
|
}
|
||||||
// 注入一些默认的提供者
|
// 在这里临时加上字体缓存器
|
||||||
registerResourceProvider(new URLResourceProvider());
|
resourceStore.addListener(FontCache.getInstance());
|
||||||
registerResourceProvider(new ClassPathResourceProvider());
|
resourceStore.init(this);
|
||||||
registerResourceProvider(new FileResourceProvider());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,33 +60,22 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceInputStream(Resource resource) {
|
public InputStream getResourceInputStream(Resource resource) {
|
||||||
for (ResourceProvider resourceProvider : resourceProviderList) {
|
return resourceProviders.getResourceInputStream(resource);
|
||||||
if (resourceProvider.supported(resource.getType())) {
|
|
||||||
InputStream resourceInputStream = resourceProvider.getResourceInputStream(resource);
|
|
||||||
if (resourceInputStream == null) {
|
|
||||||
throw new IllegalArgumentException("滑块验证码 ResourceProvider 读到的图片资源为空,providerName=["
|
|
||||||
+ resourceProvider.getName() + "], resource=[" + resource + "]");
|
|
||||||
}
|
|
||||||
return resourceInputStream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("没有找到Resource [" + resource.getType() + "]对应的资源提供者");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ResourceProvider> listResourceProviders() {
|
public List<ResourceProvider> listResourceProviders() {
|
||||||
return Collections.unmodifiableList(resourceProviderList);
|
return resourceProviders.listResourceProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerResourceProvider(ResourceProvider resourceProvider) {
|
public void registerResourceProvider(ResourceProvider resourceProvider) {
|
||||||
deleteResourceProviderByName(resourceProvider.getName());
|
resourceProviders.registerResourceProvider(resourceProvider);
|
||||||
resourceProviderList.add(resourceProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteResourceProviderByName(String name) {
|
public boolean deleteResourceProviderByName(String name) {
|
||||||
return resourceProviderList.removeIf(r -> r.getName().equals(name));
|
return resourceProviders.deleteResourceProviderByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package cloud.tianai.captcha.resource.impl;
|
|||||||
import cloud.tianai.captcha.common.constant.CommonConstant;
|
import cloud.tianai.captcha.common.constant.CommonConstant;
|
||||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||||
import cloud.tianai.captcha.common.util.ObjectUtils;
|
import cloud.tianai.captcha.common.util.ObjectUtils;
|
||||||
import cloud.tianai.captcha.resource.ResourceStore;
|
import cloud.tianai.captcha.resource.AbstractResourceStore;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
* @date 2021/8/7 15:43
|
* @date 2021/8/7 15:43
|
||||||
* @Description 默认的资源存储
|
* @Description 默认的资源存储
|
||||||
*/
|
*/
|
||||||
public class LocalMemoryResourceStore implements ResourceStore {
|
public class LocalMemoryResourceStore extends AbstractResourceStore {
|
||||||
private static final String TYPE_TAG_SPLIT_FLAG = "|";
|
private static final String TYPE_TAG_SPLIT_FLAG = "|";
|
||||||
|
|
||||||
/** 用于检索 type和tag. */
|
/** 用于检索 type和tag. */
|
||||||
@@ -23,7 +23,7 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
private Map<String, List<Resource>> resourceTagMap = new HashMap<>(2);
|
private Map<String, List<Resource>> resourceTagMap = new HashMap<>(2);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addResource(String type, Resource resource) {
|
public void doAddResource(String type, Resource resource) {
|
||||||
if (ObjectUtils.isEmpty(resource.getTag())) {
|
if (ObjectUtils.isEmpty(resource.getTag())) {
|
||||||
resource.setTag(CommonConstant.DEFAULT_TAG);
|
resource.setTag(CommonConstant.DEFAULT_TAG);
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTemplate(String type, ResourceMap template) {
|
public void doAddTemplate(String type, ResourceMap template) {
|
||||||
if (ObjectUtils.isEmpty(template.getTag())) {
|
if (ObjectUtils.isEmpty(template.getTag())) {
|
||||||
template.setTag(CommonConstant.DEFAULT_TAG);
|
template.setTag(CommonConstant.DEFAULT_TAG);
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,77 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource randomGetResourceByTypeAndTag(String type, String tag) {
|
public Resource doDeleteResource(String type, String id) {
|
||||||
|
for (Map.Entry<String, List<Resource>> entry : resourceTagMap.entrySet()) {
|
||||||
|
String k = entry.getKey();
|
||||||
|
List<Resource> v = entry.getValue();
|
||||||
|
String splitType = splitTypeTag(k)[0];
|
||||||
|
if (splitType.equals(type)) {
|
||||||
|
Iterator<Resource> iterator = v.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Resource next = iterator.next();
|
||||||
|
if (next.getId().equals(id)) {
|
||||||
|
iterator.remove();
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceMap doDeleteTemplate(String type, String id) {
|
||||||
|
for (Map.Entry<String, List<ResourceMap>> entry : templateResourceTagMap.entrySet()) {
|
||||||
|
String k = entry.getKey();
|
||||||
|
List<ResourceMap> v = entry.getValue();
|
||||||
|
String splitType = splitTypeTag(k)[0];
|
||||||
|
if (splitType.equals(type)) {
|
||||||
|
Iterator<ResourceMap> iterator = v.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
ResourceMap next = iterator.next();
|
||||||
|
if (next.getId().equals(id)) {
|
||||||
|
iterator.remove();
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Resource> listResourcesByTypeAndTag(String type, String tag) {
|
||||||
|
if (!ObjectUtils.isEmpty(tag)) {
|
||||||
|
return resourceTagMap.get(mergeTypeAndTag(type, tag));
|
||||||
|
}
|
||||||
|
List<Resource> resourceList = new ArrayList<>();
|
||||||
|
resourceTagMap.forEach((k, v) -> {
|
||||||
|
String splitType = splitTypeTag(k)[0];
|
||||||
|
if (splitType.equals(type)) {
|
||||||
|
resourceList.addAll(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return resourceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag) {
|
||||||
|
if (!ObjectUtils.isEmpty(tag)) {
|
||||||
|
return templateResourceTagMap.get(mergeTypeAndTag(type, tag));
|
||||||
|
}
|
||||||
|
List<ResourceMap> resourceMapList = new ArrayList<>();
|
||||||
|
templateResourceTagMap.forEach((k, v) -> {
|
||||||
|
String splitType = splitTypeTag(k)[0];
|
||||||
|
if (splitType.equals(type)) {
|
||||||
|
resourceMapList.addAll(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return resourceMapList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource doRandomGetResourceByTypeAndTag(String type, String tag) {
|
||||||
List<Resource> resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
|
List<Resource> resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
|
||||||
if (CollectionUtils.isEmpty(resources)) {
|
if (CollectionUtils.isEmpty(resources)) {
|
||||||
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
|
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
|
||||||
@@ -48,11 +118,20 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
return resources.get(0);
|
return resources.get(0);
|
||||||
}
|
}
|
||||||
int randomIndex = ThreadLocalRandom.current().nextInt(resources.size());
|
int randomIndex = ThreadLocalRandom.current().nextInt(resources.size());
|
||||||
|
try {
|
||||||
return resources.get(randomIndex);
|
return resources.get(randomIndex);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(0);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return doRandomGetResourceByTypeAndTag(type, tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) {
|
public ResourceMap doRandomGetTemplateByTypeAndTag(String type, String tag) {
|
||||||
List<ResourceMap> templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
|
List<ResourceMap> templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
|
||||||
if (CollectionUtils.isEmpty(templateList)) {
|
if (CollectionUtils.isEmpty(templateList)) {
|
||||||
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
|
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
|
||||||
@@ -61,7 +140,16 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
return templateList.get(0);
|
return templateList.get(0);
|
||||||
}
|
}
|
||||||
int randomIndex = ThreadLocalRandom.current().nextInt(templateList.size());
|
int randomIndex = ThreadLocalRandom.current().nextInt(templateList.size());
|
||||||
|
try {
|
||||||
return templateList.get(randomIndex);
|
return templateList.get(randomIndex);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(0);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return doRandomGetTemplateByTypeAndTag(type, tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String mergeTypeAndTag(String type, String tag) {
|
public String mergeTypeAndTag(String type, String tag) {
|
||||||
@@ -71,13 +159,17 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
return type + TYPE_TAG_SPLIT_FLAG + tag;
|
return type + TYPE_TAG_SPLIT_FLAG + tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] splitTypeTag(String k) {
|
||||||
|
return k.split("\\" + TYPE_TAG_SPLIT_FLAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void clearResources(String type, String tag) {
|
public void clearResources(String type, String tag) {
|
||||||
resourceTagMap.remove(mergeTypeAndTag(type, tag));
|
resourceTagMap.remove(mergeTypeAndTag(type, tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearAllResources() {
|
public void doClearAllResources() {
|
||||||
resourceTagMap.clear();
|
resourceTagMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +195,7 @@ public class LocalMemoryResourceStore implements ResourceStore {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearAllTemplates() {
|
public void doClearAllTemplates() {
|
||||||
templateResourceTagMap.clear();
|
templateResourceTagMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -20,8 +20,8 @@ public class ClassPathResourceProvider extends AbstractResourceProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(String type) {
|
public boolean supported(Resource resource) {
|
||||||
return NAME.equalsIgnoreCase(type);
|
return NAME.equalsIgnoreCase(resource.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public class FileResourceProvider extends AbstractResourceProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(String type) {
|
public boolean supported(Resource resource) {
|
||||||
return NAME.equalsIgnoreCase(type);
|
return NAME.equalsIgnoreCase(resource.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public class URLResourceProvider extends AbstractResourceProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(String type) {
|
public boolean supported(Resource resource) {
|
||||||
return NAME.equalsIgnoreCase(type);
|
return NAME.equalsIgnoreCase(resource.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class TACBuilderTest {
|
|||||||
template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/active.png"));
|
template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/active.png"));
|
||||||
template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/fixed.png"));
|
template1.put(StandardSliderImageCaptchaGenerator.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, "/fixed.png"));
|
||||||
|
|
||||||
ImageCaptchaApplication application = TACBuilder.builder()
|
ImageCaptchaApplication application = TACBuilder.builder(new LocalMemoryResourceStore())
|
||||||
// 加载系统自带的默认资源
|
// 加载系统自带的默认资源
|
||||||
.addDefaultTemplate()
|
.addDefaultTemplate()
|
||||||
// 设置验证码过期时间
|
// 设置验证码过期时间
|
||||||
@@ -43,11 +43,11 @@ public class TACBuilderTest {
|
|||||||
// 设置缓冲器,可提前生成验证码,用于增加并发性
|
// 设置缓冲器,可提前生成验证码,用于增加并发性
|
||||||
.cached(10, 1000, 5000, 10000L)
|
.cached(10, 1000, 5000, 10000L)
|
||||||
// 添加字体包,用于给文字点选验证码提供字体
|
// 添加字体包,用于给文字点选验证码提供字体
|
||||||
.addFont(font)
|
.addFont(new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\captcha\\手写字体\\ttf\\千图小兔体.ttf"))
|
||||||
// 设置缓存存储器,如果要支持分布式,需要把这里改成分布式缓存,比如通过redis实现的 CacheStore 缓存
|
// 设置缓存存储器,如果要支持分布式,需要把这里改成分布式缓存,比如通过redis实现的 CacheStore 缓存
|
||||||
.setCacheStore(new LocalCacheStore())
|
.setCacheStore(new LocalCacheStore())
|
||||||
// 设置资源存储器,如果想在分布式环境或者想统一管理以及扩展 实现 ResourceStore 接口,自定义
|
// 设置资源存储器,如果想在分布式环境或者想统一管理以及扩展 实现 ResourceStore 接口,自定义
|
||||||
.setResourceStore(new LocalMemoryResourceStore())
|
// .setResourceStore(new LocalMemoryResourceStore())
|
||||||
// 图片转换器,默认是将图片转换成base64格式, 背景图为jpg, 模板图为png, 如果想要扩展,可替换成自己实现的
|
// 图片转换器,默认是将图片转换成base64格式, 背景图为jpg, 模板图为png, 如果想要扩展,可替换成自己实现的
|
||||||
.setTransform(new Base64ImageTransform())
|
.setTransform(new Base64ImageTransform())
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class TACBuilderTest2 {
|
|||||||
return CaptchaInterceptor.super.beforeGenerateCaptcha(context, type, param);
|
return CaptchaInterceptor.super.beforeGenerateCaptcha(context, type, param);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addFont(font)
|
.addFont(new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\captcha\\手写字体\\ttf\\千图小兔体.ttf"))
|
||||||
.build();
|
.build();
|
||||||
CaptchaResponse<ImageCaptchaVO> response = application.generateCaptcha("WORD_IMAGE_CLICK");
|
CaptchaResponse<ImageCaptchaVO> response = application.generateCaptcha("WORD_IMAGE_CLICK");
|
||||||
System.out.println(response);
|
System.out.println(response);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class Test7 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(String type) {
|
public boolean supported(Resource type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user