重构系统项目结构, 将 tianai-captcha

tianai-captcha-springboot-starter
   tianai-captcha-web-sdk
   tianai-captcha-solon-plugin
   整合到一块
This commit is contained in:
天爱有情
2025-10-27 15:14:10 +08:00
parent af2df2c7e2
commit 5eb258215b
156 changed files with 16688 additions and 99 deletions
@@ -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();
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}