diff --git a/pom.xml b/pom.xml
index 14c6562..315b921 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,7 @@
tianai-captcha
tianai-captcha-springboot-starter
tianai-captcha-solon-plugin
+ tianai-captcha-springboot4-starter
diff --git a/tianai-captcha-springboot4-starter/pom.xml b/tianai-captcha-springboot4-starter/pom.xml
new file mode 100644
index 0000000..92e5851
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+
+ cloud.tianai.captcha
+ tianai-captcha-parent
+ ${revision}
+
+
+ tianai-captcha-springboot4-starter
+
+
+ 17
+ 17
+ UTF-8
+ 3.18.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 4.0.0
+ pom
+ import
+ true
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ compile
+ true
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ compile
+ true
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+ com.google.code.gson
+ gson
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ cloud.tianai.captcha
+ tianai-captcha
+ ${revision}
+ compile
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 4.0.0
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 17
+ 17
+ -parameters
+
+
+
+
+
\ No newline at end of file
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/CacheStoreAutoConfiguration.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/CacheStoreAutoConfiguration.java
new file mode 100644
index 0000000..7f1c37f
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/CacheStoreAutoConfiguration.java
@@ -0,0 +1,63 @@
+package cloud.tianai.captcha.spring4.autoconfiguration;
+
+import cloud.tianai.captcha.cache.CacheStore;
+import cloud.tianai.captcha.cache.impl.LocalCacheStore;
+import cloud.tianai.captcha.spring4.store.impl.RedisCacheStore;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+/**
+ * 缓存存储器的自动配置类
+ *
+ * @author lichenpark
+ */
+@AutoConfigureAfter({DataRedisAutoConfiguration.class})
+@Configuration(proxyBeanMethods = false)
+public class CacheStoreAutoConfiguration {
+
+ /**
+ * RedisCacheStoreConfiguration
+ *
+ * @author 天爱有情
+ * @since 2020/10/27 14:06
+ */
+ @Order(1)
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(StringRedisTemplate.class)
+ public static class RedisCacheStoreConfiguration {
+
+ @Bean
+ @ConditionalOnBean(StringRedisTemplate.class)
+ @ConditionalOnMissingBean(CacheStore.class)
+ public CacheStore redis(StringRedisTemplate redisTemplate) {
+ return new RedisCacheStore(redisTemplate);
+ }
+
+ }
+
+ /**
+ * LocalCacheStoreConfiguration
+ *
+ * @author 天爱有情
+ * @since 2020/10/27 14:06
+ */
+ @Order(2)
+ @Configuration(proxyBeanMethods = false)
+ public static class LocalCacheStoreConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(CacheStore.class)
+ public CacheStore local() {
+ return new LocalCacheStore();
+ }
+
+ }
+
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/ImageCaptchaAutoConfiguration.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/ImageCaptchaAutoConfiguration.java
new file mode 100644
index 0000000..7e0e917
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/ImageCaptchaAutoConfiguration.java
@@ -0,0 +1,131 @@
+package cloud.tianai.captcha.spring4.autoconfiguration;
+
+
+import cloud.tianai.captcha.application.ImageCaptchaApplication;
+import cloud.tianai.captcha.application.TACBuilder;
+import cloud.tianai.captcha.cache.CacheStore;
+import cloud.tianai.captcha.common.util.CollectionUtils;
+import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
+import cloud.tianai.captcha.generator.ImageTransform;
+import cloud.tianai.captcha.generator.impl.transform.Base64ImageTransform;
+import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
+import cloud.tianai.captcha.interceptor.CaptchaInterceptorGroup;
+import cloud.tianai.captcha.interceptor.impl.ParamCheckCaptchaInterceptor;
+import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
+import cloud.tianai.captcha.resource.ResourceProviders;
+import cloud.tianai.captcha.resource.ResourceStore;
+import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
+import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore;
+import cloud.tianai.captcha.spring4.plugins.SpringMultiImageCaptchaGenerator;
+import cloud.tianai.captcha.spring4.plugins.secondary.SecondaryVerificationApplication;
+import cloud.tianai.captcha.validator.ImageCaptchaValidator;
+import cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+import org.springframework.core.annotation.Order;
+
+/**
+ * @Author: 天爱有情
+ * @Date 2020/5/29 9:49
+ * @Description 滑块验证码自动装配
+ */
+@Slf4j
+@Order
+@Configuration
+@AutoConfigureAfter(CacheStoreAutoConfiguration.class)
+@EnableConfigurationProperties({SpringImageCaptchaProperties.class})
+public class ImageCaptchaAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ResourceStore resourceStore() {
+ return new LocalMemoryResourceStore();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ImageCaptchaResourceManager imageCaptchaResourceManager(ResourceStore resourceStore) {
+ ResourceProviders resourceProviders = new ResourceProviders();
+ return new DefaultImageCaptchaResourceManager(resourceStore, resourceProviders);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ImageTransform imageTransform() {
+ return new Base64ImageTransform();
+ }
+
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ImageCaptchaGenerator imageCaptchaTemplate(SpringImageCaptchaProperties prop,
+ ImageCaptchaResourceManager captchaResourceManager,
+ ImageTransform imageTransform,
+ BeanFactory beanFactory) {
+ return new SpringMultiImageCaptchaGenerator(captchaResourceManager, imageTransform, beanFactory);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ImageCaptchaValidator imageCaptchaValidator() {
+ return new SimpleImageCaptchaValidator();
+ }
+
+ @Bean
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ @ConditionalOnMissingBean
+ public CaptchaInterceptor captchaInterceptor() {
+ CaptchaInterceptorGroup group = new CaptchaInterceptorGroup();
+ group.addInterceptor(new ParamCheckCaptchaInterceptor());
+// group.addInterceptor(new BasicTrackCaptchaInterceptor());
+ return group;
+ }
+
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ImageCaptchaApplication imageCaptchaApplication(ImageCaptchaGenerator captchaGenerator,
+ ImageCaptchaValidator imageCaptchaValidator,
+ CacheStore cacheStore,
+ ResourceStore resourceStore,
+ SpringImageCaptchaProperties prop,
+ CaptchaInterceptor captchaInterceptor,
+ ApplicationContext applicationContext) {
+ TACBuilder tacBuilder = TACBuilder.builder(resourceStore)
+ .setGenerator(captchaGenerator)
+ .setValidator(imageCaptchaValidator)
+ .setCacheStore(cacheStore)
+ .setProp(prop)
+ .setInterceptor(captchaInterceptor);
+
+ if (prop.getInitDefaultResource()) {
+ tacBuilder.addDefaultTemplate(prop.getDefaultResourcePrefix());
+ }
+ if (!CollectionUtils.isEmpty(prop.getFontPath())) {
+ // 读取字体包
+ for (String fontPath : prop.getFontPath()) {
+ int index = fontPath.indexOf(":");
+ String[] split = index > 0 ? new String[]{fontPath.substring(0, index), fontPath.substring(index + 1)} : new String[]{"", fontPath};
+ String type = split[0];
+ String path = split[1];
+ tacBuilder.addFont(new cloud.tianai.captcha.resource.common.model.dto.Resource(type, path));
+ }
+ }
+ ImageCaptchaApplication target = tacBuilder.build();
+ if (prop.getSecondary() != null && Boolean.TRUE.equals(prop.getSecondary().getEnabled())) {
+ // 一个简单的二次验证
+ target = new SecondaryVerificationApplication(target, prop.getSecondary());
+ }
+ return target;
+ }
+
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/SecondaryVerificationProperties.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/SecondaryVerificationProperties.java
new file mode 100644
index 0000000..df199f2
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/SecondaryVerificationProperties.java
@@ -0,0 +1,12 @@
+package cloud.tianai.captcha.spring4.autoconfiguration;
+
+import lombok.Data;
+
+@Data
+public class SecondaryVerificationProperties {
+
+ private Boolean enabled = false;
+ private Long expire = 120000L;
+ private String keyPrefix = "captcha:secondary";
+
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/SpringImageCaptchaProperties.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/SpringImageCaptchaProperties.java
new file mode 100644
index 0000000..c568c6c
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/autoconfiguration/SpringImageCaptchaProperties.java
@@ -0,0 +1,32 @@
+package cloud.tianai.captcha.spring4.autoconfiguration;
+
+import cloud.tianai.captcha.application.ImageCaptchaProperties;
+import cloud.tianai.captcha.resource.DefaultBuiltInResources;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * @Author: 天爱有情
+ * @date 2020/10/19 18:41
+ * @Description 滑块验证码属性
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ConfigurationProperties(prefix = "captcha")
+public class SpringImageCaptchaProperties extends ImageCaptchaProperties {
+ /** 是否初始化默认资源. */
+ private Boolean initDefaultResource = false;
+ /** 默认资源的位置. */
+ private String defaultResourcePrefix = DefaultBuiltInResources.PATH_PREFIX;
+ /** 字体包路径. */
+ private List fontPath;
+ /** 二次验证配置. */
+ @NestedConfigurationProperty
+ private SecondaryVerificationProperties secondary;
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/exception/CaptchaValidException.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/exception/CaptchaValidException.java
new file mode 100644
index 0000000..4b9a11b
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/exception/CaptchaValidException.java
@@ -0,0 +1,41 @@
+package cloud.tianai.captcha.spring4.exception;
+
+import cloud.tianai.captcha.common.exception.ImageCaptchaException;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @Author: 天爱有情
+ * @Date 2020/6/19 16:36
+ * @Description 验证码验证失败异常
+ */
+@Getter
+@Setter
+public class CaptchaValidException extends ImageCaptchaException {
+
+ private String captchaType;
+ private Integer code;
+ public CaptchaValidException() {
+ }
+
+ public CaptchaValidException(String captchaType,String message) {
+ super(message);
+ this.captchaType = captchaType;
+ }
+ public CaptchaValidException(String captchaType,Integer code, String message) {
+ super(message);
+ this.code = code;
+ this.captchaType = captchaType;
+ }
+ public CaptchaValidException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CaptchaValidException(Throwable cause) {
+ super(cause);
+ }
+
+ public CaptchaValidException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/plugins/SpringMultiImageCaptchaGenerator.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/plugins/SpringMultiImageCaptchaGenerator.java
new file mode 100644
index 0000000..c504561
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/plugins/SpringMultiImageCaptchaGenerator.java
@@ -0,0 +1,36 @@
+package cloud.tianai.captcha.spring4.plugins;
+
+import cloud.tianai.captcha.generator.ImageCaptchaGeneratorProvider;
+import cloud.tianai.captcha.generator.ImageTransform;
+import cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator;
+import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.ListableBeanFactory;
+
+/**
+ * @Author: 天爱有情
+ * @date 2022/5/19 14:37
+ * @Description 基于spring的 多验证码生成器
+ */
+public class SpringMultiImageCaptchaGenerator extends MultiImageCaptchaGenerator {
+ private ListableBeanFactory beanFactory;
+
+ public SpringMultiImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, ImageTransform imageTransform,
+ BeanFactory beanFactory) {
+ super(imageCaptchaResourceManager, imageTransform);
+ this.beanFactory = (ListableBeanFactory) beanFactory;
+ }
+
+ @Override
+ protected void doInit() {
+ super.doInit();
+ String[] beanNamesForType = beanFactory.getBeanNamesForType(ImageCaptchaGeneratorProvider.class);
+ if (!ArrayUtils.isEmpty(beanNamesForType)) {
+ for (String beanName : beanNamesForType) {
+ ImageCaptchaGeneratorProvider provider = beanFactory.getBean(beanName, ImageCaptchaGeneratorProvider.class);
+ addImageCaptchaGeneratorProvider(provider);
+ }
+ }
+ }
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/plugins/secondary/SecondaryVerificationApplication.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/plugins/secondary/SecondaryVerificationApplication.java
new file mode 100644
index 0000000..f2e0723
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/plugins/secondary/SecondaryVerificationApplication.java
@@ -0,0 +1,58 @@
+package cloud.tianai.captcha.spring4.plugins.secondary;
+
+import cloud.tianai.captcha.application.FilterImageCaptchaApplication;
+import cloud.tianai.captcha.application.ImageCaptchaApplication;
+import cloud.tianai.captcha.common.AnyMap;
+import cloud.tianai.captcha.common.response.ApiResponse;
+import cloud.tianai.captcha.spring4.autoconfiguration.SecondaryVerificationProperties;
+import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: 天爱有情
+ * @date 2022/3/2 14:16
+ * @Description 二次验证
+ */
+public class SecondaryVerificationApplication extends FilterImageCaptchaApplication {
+ private SecondaryVerificationProperties prop;
+
+ public SecondaryVerificationApplication(ImageCaptchaApplication target, SecondaryVerificationProperties prop) {
+ super(target);
+ this.prop = prop;
+ }
+
+ @Override
+ public ApiResponse> matching(String id, ImageCaptchaTrack imageCaptchaTrack) {
+ ApiResponse> match = super.matching(id, imageCaptchaTrack);
+ if (match.isSuccess()) {
+ // 如果匹配成功, 添加二次验证记录
+ addSecondaryVerification(id, imageCaptchaTrack);
+ }
+ return match;
+ }
+
+ /**
+ * 二次缓存验证
+ * @param id id
+ * @return boolean
+ */
+ public boolean secondaryVerification(String id) {
+ Map cache = target.getCacheStore().getAndRemoveCache(getKey(id));
+ return cache != null;
+ }
+
+ /**
+ * 添加二次缓存验证记录
+ * @param id id
+ * @param imageCaptchaTrack sliderCaptchaTrack
+ */
+ protected void addSecondaryVerification(String id, ImageCaptchaTrack imageCaptchaTrack) {
+ target.getCacheStore().setCache(getKey(id), new AnyMap(), prop.getExpire(), TimeUnit.MILLISECONDS);
+ }
+
+ protected String getKey(String id) {
+ return prop.getKeyPrefix().concat(":").concat(id);
+ }
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/store/impl/RedisCacheStore.java b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/store/impl/RedisCacheStore.java
new file mode 100644
index 0000000..3f42c4b
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/java/cloud/tianai/captcha/spring4/store/impl/RedisCacheStore.java
@@ -0,0 +1,71 @@
+package cloud.tianai.captcha.spring4.store.impl;
+
+import cloud.tianai.captcha.cache.CacheStore;
+import cloud.tianai.captcha.common.AnyMap;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.util.StringUtils;
+
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: 天爱有情
+ * @date 2022/3/2 14:42
+ * @Description redis实现的缓存
+ */
+public class RedisCacheStore implements CacheStore {
+
+ private static final RedisScript SCRIPT_GET_CACHE = new DefaultRedisScript<>("local res = redis.call('get',KEYS[1]) if res == nil then return nil else redis.call('del',KEYS[1]) return res end", String.class);
+ protected StringRedisTemplate redisTemplate;
+ private Gson gson = new Gson();
+
+ public RedisCacheStore(StringRedisTemplate redisTemplate) {
+ this.redisTemplate = redisTemplate;
+ }
+
+ @Override
+ public AnyMap getCache(String key) {
+ String jsonData = redisTemplate.opsForValue().get(key);
+ if (StringUtils.isEmpty(jsonData)) {
+ return null;
+ }
+ return gson.fromJson(jsonData, new TypeToken() {
+ }.getType());
+ }
+
+ @Override
+ public AnyMap getAndRemoveCache(String key) {
+ String json = redisTemplate.execute(SCRIPT_GET_CACHE, Collections.singletonList(key));
+ if (org.apache.commons.lang3.StringUtils.isBlank(json)) {
+ return null;
+ }
+ return gson.fromJson(json, new TypeToken() {
+ }.getType());
+ }
+
+ @Override
+ public boolean setCache(String key, AnyMap data, Long expire, TimeUnit timeUnit) {
+ redisTemplate.opsForValue().set(key, gson.toJson(data), expire, timeUnit);
+ return true;
+ }
+
+ @Override
+ public Long incr(String key, long delta, Long expire, TimeUnit timeUnit) {
+ Long increment = redisTemplate.opsForValue().increment(key, delta);
+ redisTemplate.expire(key, expire, timeUnit);
+ return increment;
+ }
+
+ @Override
+ public Long getLong(String key) {
+ String value = redisTemplate.opsForValue().get(key);
+ if (value == null) {
+ return null;
+ }
+ return Long.valueOf(value);
+ }
+}
diff --git a/tianai-captcha-springboot4-starter/src/main/resources/META-INF/spring.factories b/tianai-captcha-springboot4-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..2279330
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cloud.tianai.captcha.spring4.autoconfiguration.CacheStoreAutoConfiguration,\
+ cloud.tianai.captcha.spring4.autoconfiguration.ImageCaptchaAutoConfiguration
\ No newline at end of file
diff --git a/tianai-captcha-springboot4-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/tianai-captcha-springboot4-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..fb1e5de
--- /dev/null
+++ b/tianai-captcha-springboot4-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,2 @@
+cloud.tianai.captcha.spring4.autoconfiguration.CacheStoreAutoConfiguration
+cloud.tianai.captcha.spring4.autoconfiguration.ImageCaptchaAutoConfiguration