重构系统项目结构, 将 tianai-captcha
tianai-captcha-springboot-starter tianai-captcha-web-sdk tianai-captcha-solon-plugin 整合到一块
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/12/16 16:52
|
||||
* @Description 抽象的ResourceProvider
|
||||
*/
|
||||
public abstract class AbstractResourceProvider implements ResourceProvider {
|
||||
@Override
|
||||
public InputStream getResourceInputStream(Resource data) {
|
||||
InputStream resourceInputStream = doGetResourceInputStream(data);
|
||||
if (resourceInputStream == null) {
|
||||
throw new IllegalArgumentException("无法读到指定的资源[" + getName() + "]" + data);
|
||||
}
|
||||
return resourceInputStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 Resource 获取 InputStream
|
||||
*
|
||||
* @param data data
|
||||
* @return InputStream
|
||||
*/
|
||||
public abstract InputStream doGetResourceInputStream(Resource data);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2025/6/13 16:43
|
||||
* @Description 具有CRUD属性的资源存储器
|
||||
*/
|
||||
public interface CrudResourceStore extends ResourceStore {
|
||||
/**
|
||||
* 添加资源
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param resource 资源
|
||||
*/
|
||||
void addResource(String type, Resource resource);
|
||||
|
||||
|
||||
/**
|
||||
* 添加模板
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param 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);
|
||||
|
||||
|
||||
/**
|
||||
* 清除所有内置模板
|
||||
*/
|
||||
void clearAllTemplates();
|
||||
|
||||
/**
|
||||
* 清除所有内置资源
|
||||
*/
|
||||
void clearAllResources();
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
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<CrudResourceStore>> 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);
|
||||
});
|
||||
|
||||
// 字体包
|
||||
defaultTemplateResource.put(FontCache.FONT_TYPE, resourceStore -> {
|
||||
resourceStore.addResource(FontCache.FONT_TYPE,new Resource(type, finalPathPrefix.concat("/fonts/SIMSUN.TTC")));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void addDefaultTemplate(String type, ResourceStore resourceStore) {
|
||||
if (resourceStore instanceof CrudResourceStore) {
|
||||
Consumer<CrudResourceStore> resourceStoreConsumer = defaultTemplateResource.get(type);
|
||||
if (resourceStoreConsumer == null) {
|
||||
return;
|
||||
}
|
||||
resourceStoreConsumer.accept((CrudResourceStore) resourceStore);
|
||||
}
|
||||
}
|
||||
|
||||
public void addDefaultTemplate(ResourceStore resourceStore) {
|
||||
if (resourceStore instanceof CrudResourceStore) {
|
||||
defaultTemplateResource.forEach((type, consumer) -> {
|
||||
consumer.accept((CrudResourceStore) resourceStore);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.generator.common.FontWrapper;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
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 ResourceStore {
|
||||
|
||||
|
||||
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 FontCache(ResourceStore resourceStore) {
|
||||
this.resourceStore = resourceStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ImageCaptchaResourceManager resourceManager) {
|
||||
resourceStore.init(resourceManager);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String calcId(Resource resource) {
|
||||
// 缓存id, 避免重复加载。 多个验证码可能使用同一个字体, 这里不使用资源ID作为缓存ID, 而是使用type+data作为缓存ID。
|
||||
return resource.getType() + "_" + resource.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
|
||||
List<Resource> resources = resourceStore.randomGetResourceByTypeAndTag(type, tag, quantity);
|
||||
// 字体增强
|
||||
if (FONT_TYPE.equalsIgnoreCase(type)) {
|
||||
for (Resource resource : resources) {
|
||||
FontWrapper fontWrapper = fontMap.computeIfAbsent(calcId(resource), v -> getFont(resource));
|
||||
resource.setExtra(fontWrapper);
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
|
||||
return resourceStore.randomGetTemplateByTypeAndTag(type, tag, quantity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceStore getTarget() {
|
||||
return resourceStore;
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 15:26
|
||||
* @Description 验证码图片资源管理器
|
||||
*/
|
||||
public interface ImageCaptchaResourceManager {
|
||||
|
||||
/**
|
||||
* 随机获取某个模板
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param tag 二级过滤,可以为空
|
||||
* @return Map<String, Resource>
|
||||
*/
|
||||
ResourceMap randomGetTemplate(String type, String tag);
|
||||
|
||||
/**
|
||||
* 随机获取某个资源对象
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param tag 二级过滤,可以为空
|
||||
* @return Resource
|
||||
*/
|
||||
Resource randomGetResource(String type, String tag);
|
||||
|
||||
|
||||
/**
|
||||
* 随机获取某个模板
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param tag 二级过滤,可以为空
|
||||
* @param quantity 一次性获取的数量
|
||||
* @return Map<String, Resource>
|
||||
*/
|
||||
List<ResourceMap> randomGetTemplate(String type, String tag, Integer quantity);
|
||||
|
||||
/**
|
||||
* 随机获取某个资源对象
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param tag 二级过滤,可以为空
|
||||
* @param quantity 一次性获取的数量
|
||||
* @return Resource
|
||||
*/
|
||||
List<Resource> randomGetResource(String type, String tag, Integer quantity);
|
||||
|
||||
/**
|
||||
* 获取真正的资源流通过资源对象
|
||||
*
|
||||
* @param resource resource
|
||||
* @return InputStream
|
||||
*/
|
||||
InputStream getResourceInputStream(Resource resource);
|
||||
|
||||
/**
|
||||
* 获取所有资源提供者
|
||||
*
|
||||
* @return List<ResourceProvider>
|
||||
*/
|
||||
List<ResourceProvider> listResourceProviders();
|
||||
|
||||
/**
|
||||
* 注册资源提供者
|
||||
*
|
||||
* @param resourceProvider 资源提供者
|
||||
*/
|
||||
void registerResourceProvider(ResourceProvider resourceProvider);
|
||||
|
||||
/**
|
||||
* 删除资源提供者
|
||||
*
|
||||
* @param name 资源提供者名称
|
||||
* @return ResourceProvider
|
||||
*/
|
||||
boolean deleteResourceProviderByName(String name);
|
||||
|
||||
/**
|
||||
* 设置资源存储
|
||||
*
|
||||
* @param resourceStore resourceStore
|
||||
*/
|
||||
void setResourceStore(ResourceStore resourceStore);
|
||||
|
||||
/**
|
||||
* 获取资源存储
|
||||
*
|
||||
* @return ResourceStore
|
||||
*/
|
||||
ResourceStore getResourceStore();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 15:07
|
||||
* @Description 资源提供者
|
||||
*/
|
||||
public interface ResourceProvider {
|
||||
|
||||
/**
|
||||
* 获取资源
|
||||
*
|
||||
* @param data data
|
||||
* @return InputStream
|
||||
*/
|
||||
InputStream getResourceInputStream(Resource data);
|
||||
|
||||
/**
|
||||
* 是否支持
|
||||
*
|
||||
* @param resource resource
|
||||
* @return boolean
|
||||
*/
|
||||
boolean supported(Resource resource);
|
||||
|
||||
/**
|
||||
* 放弃资源提供者名称
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getName();
|
||||
}
|
||||
@@ -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() + "]对应的资源提供者");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cloud.tianai.captcha.resource;
|
||||
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/5/7 9:04
|
||||
* @Description 资源存储
|
||||
*/
|
||||
public interface ResourceStore {
|
||||
|
||||
void init(ImageCaptchaResourceManager resourceManager);
|
||||
|
||||
/**
|
||||
* 随机获取某个资源
|
||||
*
|
||||
* @param type type
|
||||
* @return Resource
|
||||
*/
|
||||
List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity);
|
||||
|
||||
/**
|
||||
* 随机获取某个模板通过type
|
||||
*
|
||||
* @param type type
|
||||
* @return Map<String, Resource>
|
||||
*/
|
||||
List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag,Integer quantity);
|
||||
|
||||
default ResourceStore getTarget() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package cloud.tianai.captcha.resource.common.model.dto;
|
||||
|
||||
import cloud.tianai.captcha.common.util.UUIDUtils;
|
||||
import cloud.tianai.captcha.resource.ResourceProvider;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 15:15
|
||||
* @Description 资源对象
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class Resource {
|
||||
/** 唯一ID. */
|
||||
private String id;
|
||||
/** 类型. */
|
||||
private String type;
|
||||
/** 数据,传输给 {@link ResourceProvider} 的参数 */
|
||||
public String data;
|
||||
/** 标签. */
|
||||
private String tag;
|
||||
/** 提示. */
|
||||
private String tip;
|
||||
/** 扩展. */
|
||||
private Object extra;
|
||||
|
||||
public Resource(String type, String data) {
|
||||
this(type, data, null);
|
||||
}
|
||||
|
||||
public Resource(String type, String data, String tag) {
|
||||
this(type, data, tag, null);
|
||||
}
|
||||
|
||||
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.data = data;
|
||||
this.tag = tag;
|
||||
this.tip = tip;
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cloud.tianai.captcha.resource.common.model.dto;
|
||||
|
||||
import cloud.tianai.captcha.common.util.UUIDUtils;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/12/30 9:23
|
||||
* @Description 存储一组Resource的Map, 增加tag标记
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class ResourceMap {
|
||||
/** 唯一ID. */
|
||||
private String id;
|
||||
private Map<String, Resource> resourceMap;
|
||||
private String tag;
|
||||
|
||||
public ResourceMap(String tag) {
|
||||
this(tag, 10);
|
||||
}
|
||||
|
||||
public ResourceMap(String tag, int initialCapacity) {
|
||||
this(UUIDUtils.getUUID(), tag, initialCapacity);
|
||||
}
|
||||
|
||||
public ResourceMap(String id, String tag, int initialCapacity) {
|
||||
this.tag = tag;
|
||||
this.resourceMap = new HashMap<>(initialCapacity);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceMap(int initialCapacity) {
|
||||
this(null, initialCapacity);
|
||||
}
|
||||
|
||||
public ResourceMap() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private Map<String, Resource> getResourceMapOfCreate() {
|
||||
if (resourceMap == null) {
|
||||
resourceMap = new HashMap<>(2);
|
||||
}
|
||||
return resourceMap;
|
||||
}
|
||||
|
||||
// ================== Map ==================
|
||||
|
||||
public Resource put(String key, Resource value) {
|
||||
return getResourceMapOfCreate().put(key, value);
|
||||
}
|
||||
|
||||
public Resource get(Object key) {
|
||||
return getResourceMapOfCreate().get(key);
|
||||
}
|
||||
|
||||
public Resource remove(Object key) {
|
||||
return getResourceMapOfCreate().remove(key);
|
||||
}
|
||||
|
||||
public Collection<Resource> values() {
|
||||
return getResourceMapOfCreate().values();
|
||||
}
|
||||
|
||||
public void forEach(BiConsumer<String, Resource> action) {
|
||||
getResourceMapOfCreate().forEach(action);
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package cloud.tianai.captcha.resource.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import 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.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 15:35
|
||||
* @Description 默认的滑块验证码资源管理
|
||||
*/
|
||||
public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceManager {
|
||||
|
||||
/** 资源存储. */
|
||||
private ResourceStore resourceStore;
|
||||
/** 资源转换 转换为stream流. */
|
||||
@Getter
|
||||
private ResourceProviders resourceProviders;
|
||||
|
||||
public DefaultImageCaptchaResourceManager() {
|
||||
init();
|
||||
}
|
||||
|
||||
public DefaultImageCaptchaResourceManager(ResourceStore resourceStore, ResourceProviders resourceProviders) {
|
||||
this.resourceStore = resourceStore;
|
||||
this.resourceProviders = resourceProviders;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (this.resourceStore == null) {
|
||||
this.resourceStore = new LocalMemoryResourceStore();
|
||||
}
|
||||
// 在这里临时加上字体缓存器
|
||||
resourceStore = new FontCache(resourceStore);
|
||||
resourceStore.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceMap randomGetTemplate(String type, String tag) {
|
||||
return randomGetTemplate(type, tag, 1).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource randomGetResource(String type, String tag) {
|
||||
return randomGetResource(type, tag, 1).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceMap> randomGetTemplate(String type, String tag, Integer quantity) {
|
||||
List<ResourceMap> resourceMaps = resourceStore.randomGetTemplateByTypeAndTag(type, tag, quantity);
|
||||
if (CollectionUtils.isEmpty(resourceMaps) || resourceMaps.size() != quantity) {
|
||||
throw new IllegalStateException("随机获取**模板**错误,获取到的数量和指定数量不一致," +
|
||||
" 指定获取数量[" + quantity + "],获取到的数据:[" + Optional.ofNullable(resourceMaps).orElse(Collections.emptyList()).size() + "], " +
|
||||
"[type:" + type + ",tag:" + tag + "]");
|
||||
}
|
||||
return resourceMaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> randomGetResource(String type, String tag, Integer quantity) {
|
||||
List<Resource> resources = resourceStore.randomGetResourceByTypeAndTag(type, tag, quantity);
|
||||
if (CollectionUtils.isEmpty(resources) || resources.size() != quantity) {
|
||||
throw new IllegalStateException("随机获取**资源**错误,获取到的数量和指定数量不一致," +
|
||||
" 指定获取数量[" + quantity + "],获取到的数据:[" + Optional.ofNullable(resources).orElse(Collections.emptyList()).size() + "], " +
|
||||
"[type:" + type + ",tag:" + tag + "]");
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InputStream getResourceInputStream(Resource resource) {
|
||||
return resourceProviders.getResourceInputStream(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceProvider> listResourceProviders() {
|
||||
return resourceProviders.listResourceProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResourceProvider(ResourceProvider resourceProvider) {
|
||||
resourceProviders.registerResourceProvider(resourceProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteResourceProviderByName(String name) {
|
||||
return resourceProviders.deleteResourceProviderByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceStore(ResourceStore resourceStore) {
|
||||
this.resourceStore = resourceStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceStore getResourceStore() {
|
||||
return resourceStore;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
package cloud.tianai.captcha.resource.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CommonConstant;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.common.util.ObjectUtils;
|
||||
import cloud.tianai.captcha.resource.CrudResourceStore;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 15:43
|
||||
* @Description 默认的资源存储
|
||||
*/
|
||||
public class LocalMemoryResourceStore implements CrudResourceStore {
|
||||
/** 用于检索 type和tag. */
|
||||
private final Map<String, Map<String, List<ResourceMap>>> templateResourceTagMap = new HashMap<>(2);
|
||||
private final Map<String, Map<String, List<Resource>>> resourceTagMap = new HashMap<>(2);
|
||||
|
||||
|
||||
private void ensureTypeTagMapExists(Map<String, Map<String, List<Resource>>> map, String type, String tag) {
|
||||
map.computeIfAbsent(type, k -> new HashMap<>())
|
||||
.computeIfAbsent(tag, k -> new ArrayList<>(20));
|
||||
}
|
||||
|
||||
private void ensureTypeTagMapExistsForTemplate(Map<String, Map<String, List<ResourceMap>>> map, String type, String tag) {
|
||||
map.computeIfAbsent(type, k -> new HashMap<>())
|
||||
.computeIfAbsent(tag, k -> new ArrayList<>(2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResource(String type, Resource resource) {
|
||||
if (ObjectUtils.isEmpty(resource.getTag())) {
|
||||
resource.setTag(CommonConstant.DEFAULT_TAG);
|
||||
}
|
||||
ensureTypeTagMapExists(resourceTagMap, type, resource.getTag());
|
||||
resourceTagMap.get(type).get(resource.getTag()).add(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTemplate(String type, ResourceMap template) {
|
||||
if (ObjectUtils.isEmpty(template.getTag())) {
|
||||
template.setTag(CommonConstant.DEFAULT_TAG);
|
||||
}
|
||||
ensureTypeTagMapExistsForTemplate(templateResourceTagMap, type, template.getTag());
|
||||
templateResourceTagMap.get(type).get(template.getTag()).add(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource deleteResource(String type, String id) {
|
||||
Map<String, List<Resource>> tagMap = resourceTagMap.get(type);
|
||||
if (tagMap == null) return null;
|
||||
|
||||
for (List<Resource> resources : tagMap.values()) {
|
||||
Iterator<Resource> iterator = resources.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Resource res = iterator.next();
|
||||
if (res.getId().equals(id)) {
|
||||
iterator.remove();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceMap deleteTemplate(String type, String id) {
|
||||
Map<String, List<ResourceMap>> tagMap = templateResourceTagMap.get(type);
|
||||
if (tagMap == null) return null;
|
||||
|
||||
for (List<ResourceMap> templates : tagMap.values()) {
|
||||
Iterator<ResourceMap> iterator = templates.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ResourceMap temp = iterator.next();
|
||||
if (temp.getId().equals(id)) {
|
||||
iterator.remove();
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> listResourcesByTypeAndTag(String type, String tag) {
|
||||
if (!ObjectUtils.isEmpty(tag)) {
|
||||
Map<String, List<Resource>> tagMap = resourceTagMap.get(type);
|
||||
return tagMap == null ? Collections.emptyList() : tagMap.getOrDefault(tag, Collections.emptyList());
|
||||
}
|
||||
List<Resource> result = new ArrayList<>();
|
||||
Map<String, List<Resource>> tagMap = resourceTagMap.get(type);
|
||||
if (tagMap != null) {
|
||||
for (List<Resource> list : tagMap.values()) {
|
||||
result.addAll(list);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag) {
|
||||
if (!ObjectUtils.isEmpty(tag)) {
|
||||
Map<String, List<ResourceMap>> tagMap = templateResourceTagMap.get(type);
|
||||
return tagMap == null ? Collections.emptyList() : tagMap.getOrDefault(tag, Collections.emptyList());
|
||||
}
|
||||
List<ResourceMap> result = new ArrayList<>();
|
||||
Map<String, List<ResourceMap>> tagMap = templateResourceTagMap.get(type);
|
||||
if (tagMap != null) {
|
||||
for (List<ResourceMap> list : tagMap.values()) {
|
||||
result.addAll(list);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ImageCaptchaResourceManager resourceManager) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
|
||||
List<Resource> resources = listResourcesByTypeAndTag(type, tag);
|
||||
if (CollectionUtils.isEmpty(resources)) {
|
||||
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type + ",tag:" + tag);
|
||||
}
|
||||
int size = resources.size();
|
||||
if (quantity > size) {
|
||||
throw new IllegalArgumentException("请求的资源数量超过可用资源总数");
|
||||
}
|
||||
|
||||
Set<Integer> indexes = new HashSet<>(quantity);
|
||||
while (indexes.size() < quantity) {
|
||||
indexes.add(ThreadLocalRandom.current().nextInt(size));
|
||||
}
|
||||
|
||||
List<Resource> result = new ArrayList<>(quantity);
|
||||
for (int index : indexes) {
|
||||
result.add(resources.get(index));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
|
||||
List<ResourceMap> templates = listTemplatesByTypeAndTag(type, tag);
|
||||
if (CollectionUtils.isEmpty(templates)) {
|
||||
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type + ",tag:" + tag);
|
||||
}
|
||||
int size = templates.size();
|
||||
if (quantity > size) {
|
||||
throw new IllegalArgumentException("请求的模板数量超过可用模板总数");
|
||||
}
|
||||
|
||||
Set<Integer> indexes = new HashSet<>(quantity);
|
||||
while (indexes.size() < quantity) {
|
||||
indexes.add(ThreadLocalRandom.current().nextInt(size));
|
||||
}
|
||||
|
||||
List<ResourceMap> result = new ArrayList<>(quantity);
|
||||
for (int index : indexes) {
|
||||
result.add(templates.get(index));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllResources() {
|
||||
resourceTagMap.clear();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clearAllTemplates() {
|
||||
templateResourceTagMap.clear();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package cloud.tianai.captcha.resource.impl.provider;
|
||||
|
||||
import cloud.tianai.captcha.resource.AbstractResourceProvider;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 16:07
|
||||
* @Description classPath
|
||||
*/
|
||||
public class ClassPathResourceProvider extends AbstractResourceProvider {
|
||||
|
||||
public static final String NAME = "classpath";
|
||||
|
||||
public static ClassLoader classLoader;
|
||||
|
||||
@Override
|
||||
public InputStream doGetResourceInputStream(Resource data) {
|
||||
if (classLoader == null) {
|
||||
return getClassLoader().getResourceAsStream(data.getData());
|
||||
}
|
||||
return classLoader.getResourceAsStream(data.getData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supported(Resource resource) {
|
||||
return NAME.equalsIgnoreCase(resource.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
private static ClassLoader getClassLoader() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = ClassPathResourceProvider.getClassLoader();
|
||||
}
|
||||
if (classLoader == null) {
|
||||
classLoader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
public static void setClassLoader(ClassLoader classLoader) {
|
||||
ClassPathResourceProvider.classLoader = classLoader;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package cloud.tianai.captcha.resource.impl.provider;
|
||||
|
||||
import cloud.tianai.captcha.resource.AbstractResourceProvider;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/2/21 14:43
|
||||
* @Description file
|
||||
*/
|
||||
public class FileResourceProvider extends AbstractResourceProvider {
|
||||
|
||||
public static final String NAME = "file";
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public InputStream doGetResourceInputStream(Resource data) {
|
||||
FileInputStream fileInputStream = new FileInputStream(data.getData());
|
||||
return fileInputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supported(Resource resource) {
|
||||
return NAME.equalsIgnoreCase(resource.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package cloud.tianai.captcha.resource.impl.provider;
|
||||
|
||||
import cloud.tianai.captcha.resource.AbstractResourceProvider;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2021/8/7 16:05
|
||||
* @Description url
|
||||
*/
|
||||
public class URLResourceProvider extends AbstractResourceProvider {
|
||||
|
||||
public static final String NAME = "URL";
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public InputStream doGetResourceInputStream(Resource data) {
|
||||
URL url = new URL(data.getData());
|
||||
return url.openStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supported(Resource resource) {
|
||||
return NAME.equalsIgnoreCase(resource.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user