diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4e5b56b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,233 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +必须用中文回复我 +## Project Overview + +tianai-captcha (天爱验证码/TAC) is a Java-based behavioral CAPTCHA library supporting multiple verification types: +- **SLIDER**: Slide-to-fit puzzle captcha +- **ROTATE**: Rotation verification captcha +- **CONCAT**: Slide-to-restore captcha +- **WORD_IMAGE_CLICK**: Text-click verification captcha + +The project is a multi-module Maven project with Java 8 compatibility. + +## Build Commands + +```bash +# Build the entire project (skips tests by default) +mvn clean install + +# Build without skipping tests +mvn clean install -DskipTests=false + +# Build specific module +cd tianai-captcha +mvn clean install + +# Package for deployment +mvn clean package + +# Generate javadoc +mvn javadoc:javadoc +``` + +## Testing + +```bash +# Run all tests +mvn test + +# Run tests for specific module +cd tianai-captcha +mvn test + +# Run a specific test class +mvn test -Dtest=TACBuilderTest + +# Run a specific test method +mvn test -Dtest=TACBuilderTest#testMethod +``` + +## Project Structure + +### Modules + +1. **tianai-captcha** (core module) + - Core captcha generation and validation logic + - Generator implementations for each captcha type + - Resource management system + - Cache and storage abstractions + +2. **tianai-captcha-springboot-starter** + - Spring Boot auto-configuration + - Redis integration support + - Configuration properties binding + +3. **tianai-captcha-solon-plugin** + - Solon framework integration + +4. **tianai-captcha-web-sdk** + - Frontend JavaScript SDK + +## Core Architecture + +### Key Components + +**ImageCaptchaApplication** (cloud.tianai.captcha.application.ImageCaptchaApplication) +- Main entry point for captcha operations +- Handles captcha generation via `generateCaptcha(type)` +- Handles validation via `matching(id, matchParam)` +- Manages lifecycle of generators, validators, and cache + +**TACBuilder** (cloud.tianai.captcha.application.TACBuilder) +- Builder pattern for constructing ImageCaptchaApplication +- Fluent API for configuration +- Key methods: + - `addDefaultTemplate()`: Load built-in templates + - `addResource(type, resource)`: Add background images + - `addTemplate(type, resourceMap)`: Add custom templates + - `addFont(resource)`: Add fonts for WORD_IMAGE_CLICK + - `cached(size, waitTime, period, expireTime)`: Enable pre-generation cache + - `setCacheStore(store)`: Set cache implementation (local or distributed) + - `setResourceStore(store)`: Set resource storage + - `setTransform(transform)`: Set image transformation (default: Base64) + +### Generator Layer + +**MultiImageCaptchaGenerator** (cloud.tianai.captcha.generator.impl.MultiImageCaptchaGenerator) +- Delegates to specific generators based on captcha type +- Manages resource loading and image transformation + +**Specific Generators**: +- `StandardSliderImageCaptchaGenerator`: Generates slider puzzles +- `StandardRotateImageCaptchaGenerator`: Generates rotation captchas +- `StandardConcatImageCaptchaGenerator`: Generates slide-to-restore captchas +- `StandardWordClickImageCaptchaGenerator`: Generates text-click captchas + +**CacheImageCaptchaGenerator** (cloud.tianai.captcha.generator.impl.CacheImageCaptchaGenerator) +- Wrapper that pre-generates captchas for improved performance +- Configurable cache size and refresh intervals + +### Validator Layer + +**ImageCaptchaValidator** (cloud.tianai.captcha.validator.ImageCaptchaValidator) +- Interface for captcha validation logic +- Default implementation: `SimpleImageCaptchaValidator` + +**BasicCaptchaTrackValidator** (cloud.tianai.captcha.validator.impl.BasicCaptchaTrackValidator) +- Validates mouse/touch track data +- Checks for suspicious behavior patterns + +### Resource Management + +**ResourceStore** (cloud.tianai.captcha.resource.ResourceStore) +- Abstraction for storing captcha resources (images, templates) +- Implementations: + - `LocalMemoryResourceStore`: In-memory storage + - Custom implementations for distributed scenarios + +**ImageCaptchaResourceManager** (cloud.tianai.captcha.resource.ImageCaptchaResourceManager) +- Manages resource loading and retrieval +- Default implementation: `DefaultImageCaptchaResourceManager` + +**Resource** (cloud.tianai.captcha.resource.common.model.dto.Resource) +- Represents a resource with type and location +- Types: "classpath", "file", "url" + +### Cache Layer + +**CacheStore** (cloud.tianai.captcha.cache.CacheStore) +- Abstraction for caching captcha data +- Implementations: + - `LocalCacheStore`: Local in-memory cache + - Redis-based implementation in Spring Boot starter + +### Image Transformation + +**ImageTransform** (cloud.tianai.captcha.generator.ImageTransform) +- Converts generated images to desired format +- Default: `Base64ImageTransform` (JPG for background, PNG for template) + +## Configuration + +### Non-Spring Projects + +Use TACBuilder to configure: + +```java +ImageCaptchaApplication app = TACBuilder.builder() + .addDefaultTemplate() + .addResource("SLIDER", new Resource("classpath", "path/to/image.jpg")) + .cached(20, 5000, 2000, 120000L) + .build(); +``` + +### Spring Boot Projects + +Configure via application.yml: + +```yaml +captcha: + prefix: captcha # Cache key prefix + expire: + default: 120000 # Default expiration (ms) + WORD_IMAGE_CLICK: 180000 # Type-specific expiration + init-default-resource: false # Load built-in resources + local-cache-enabled: true # Enable pre-generation cache + local-cache-size: 20 # Cache size + local-cache-wait-time: 5000 # Wait time on cache miss (ms) + local-cache-period: 2000 # Cache refresh interval (ms) + font-path: # Font files for WORD_IMAGE_CLICK + - classpath:font/simhei.ttf +``` + +## Important Constants + +**CaptchaTypeConstant** (cloud.tianai.captcha.common.constant.CaptchaTypeConstant) +- `SLIDER`: Slide-to-fit captcha +- `ROTATE`: Rotation captcha +- `CONCAT`: Slide-to-restore captcha +- `WORD_IMAGE_CLICK`: Text-click captcha + +**ParamKeyEnum** (cloud.tianai.captcha.generator.common.model.dto.ParamKeyEnum) +- `TOLERANT`: Configurable tolerance value for validation (added in recent commit) + +## Key Data Flow + +### Generation Flow +1. Client calls `ImageCaptchaApplication.generateCaptcha(type)` +2. `MultiImageCaptchaGenerator` selects appropriate generator +3. Generator loads resources via `ImageCaptchaResourceManager` +4. Generator creates captcha with random parameters +5. `ImageTransform` converts images to Base64 (or custom format) +6. Captcha data cached in `CacheStore` with unique ID +7. Returns `ImageCaptchaVO` with ID and image data + +### Validation Flow +1. Client submits ID and track data via `matching(id, matchParam)` +2. Application retrieves cached captcha data by ID +3. `ImageCaptchaValidator` validates track against expected answer +4. `BasicCaptchaTrackValidator` checks track behavior patterns +5. Returns `ApiResponse` with success/failure status + +## Extension Points + +- **Custom Generators**: Implement `ImageCaptchaGenerator` interface +- **Custom Validators**: Implement `ImageCaptchaValidator` interface +- **Custom Cache**: Implement `CacheStore` (e.g., Redis, Memcached) +- **Custom Resources**: Implement `ResourceStore` for centralized resource management +- **Custom Transform**: Implement `ImageTransform` for different image formats +- **Interceptors**: Implement `CaptchaInterceptor` for pre/post processing + +## Recent Changes + +- Added support for Spring Boot 4 (commit: db0603a, 7c8730f) +- Fixed resource leak issues (commits: 16e517c, 29279e8) +- Added configurable tolerance value via `ParamKeyEnum.TOLERANT` (commit: a4f8a99) + +## Documentation + +- Online Demo: http://captcha.tianai.cloud +- Online Documentation: http://doc.captcha.tianai.cloud +- License: MulanPSL-2.0 diff --git a/pom.xml b/pom.xml index 14c6562..23af062 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ - 1.5.3 + 1.5.4 1.8 true diff --git a/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java b/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java index 9920e0b..f6490b1 100644 --- a/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java +++ b/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/AbstractClickImageCaptchaGenerator.java @@ -65,7 +65,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa } // 随机获取点击图片 - ClickImageCheckDefinition.ImgWrapper imgWrapper = getClickImg(param, clickResource, null); + ClickImageCheckDefinition.ImgWrapper imgWrapper = getClickImg(param, clickResource, null, bgImage); BufferedImage image = imgWrapper.getImage(); // 增加功能,是否需要扭曲图片 image = obfuscateImage(image, param); @@ -143,7 +143,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa * @param tip 提示数据,根据改数据生成图片 * @return ImgWrapper */ - public abstract ClickImageCheckDefinition.ImgWrapper getClickImg(GenerateParam param, Resource tip, Color randomColor); + public abstract ClickImageCheckDefinition.ImgWrapper getClickImg(GenerateParam param, Resource tip, Color randomColor, BufferedImage bgImage); @Data diff --git a/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java b/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java index 632ef1c..d516fa5 100644 --- a/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java +++ b/tianai-captcha/src/main/java/cloud/tianai/captcha/generator/impl/StandardWordClickImageCaptchaGenerator.java @@ -92,7 +92,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa } @Override - public ClickImageCheckDefinition.ImgWrapper getClickImg(GenerateParam param, Resource tip, Color randomColor) { + public ClickImageCheckDefinition.ImgWrapper getClickImg(GenerateParam param, Resource tip, Color randomColor, BufferedImage bgImage) { if (randomColor == null) { ThreadLocalRandom random = ThreadLocalRandom.current(); randomColor = CaptchaImageUtils.getRandomColor(random); @@ -100,7 +100,7 @@ public class StandardWordClickImageCaptchaGenerator extends AbstractClickImageCa // 随机角度 int randomDeg = randomInt(0, 85); // 缩放 - double factor = 600d; + double factor = (double) bgImage.getWidth(null) / 600d; FontWrapper fontWrapper = randomFont(param); Font font = fontWrapper.getFont((float) (FontWrapper.DEFAULT_FONT_SIZE * factor)); diff --git a/tianai-captcha/src/test/java/example/readme/TACBuilderTest.java b/tianai-captcha/src/test/java/example/readme/TACBuilderTest.java index 9747f64..2e07962 100644 --- a/tianai-captcha/src/test/java/example/readme/TACBuilderTest.java +++ b/tianai-captcha/src/test/java/example/readme/TACBuilderTest.java @@ -14,48 +14,100 @@ import cloud.tianai.captcha.resource.common.model.dto.Resource; import cloud.tianai.captcha.resource.common.model.dto.ResourceMap; import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore; import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider; +import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack; import java.awt.*; +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; + public class TACBuilderTest { + + public void test() { + ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 2); + // 滑块轮廓图片 + template1.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource("classpath", "META-INF/cut-image/template/slider_1/active.png")); + // 滑块凹槽图片 + template1.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource("classpath", "META-INF/cut-image/template/slider_1/fixed.png")); + + // 给旋转验证码配置模板 + ResourceMap template2 = new ResourceMap(DEFAULT_TAG, 2); + // 旋转验证码轮廓图片 + template2.put(TEMPLATE_ACTIVE_IMAGE_NAME, new Resource("classpath", "META-INF/cut-image/template/rotate_1/active.png")); + // 旋转验证码凹槽图片 + template2.put(TEMPLATE_FIXED_IMAGE_NAME, new Resource("classpath", "META-INF/cut-image/template/rotate_1/fixed.png")); + + ImageCaptchaApplication application = TACBuilder.builder() + // 设置资源存储器,默认是 LocalMemoryResourceStore + .setResourceStore(new LocalMemoryResourceStore()) + // 配置模板 + .addTemplate(CaptchaTypeConstant.SLIDER,template1) + .addTemplate(CaptchaTypeConstant.ROTATE,template2) + // ...其他配置 + .build(); + } + public static void main(String[] args) throws InterruptedException { - Font font= null; + Font font = null; // ResourceMap template1 = new ResourceMap("default", 4); // 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")); ImageCaptchaApplication application = TACBuilder.builder() + // 设置资源存储器,默认是 LocalMemoryResourceStore .setResourceStore(new LocalMemoryResourceStore()) - // 加载系统自带的默认资源 + // 加载系统自带的默认资源(系统内置了几个滑块验证码缺口模板图,调用此函数加载) .addDefaultTemplate() - // 设置验证码过期时间 + // 设置验证码过期时间, 单位毫秒, default 是默认验证码过期时间,当前设置为10秒, + // 可以自定义某些验证码类型单独的过期时间, 比如把点选验证码的过期时间设置为60秒 .expire("default", 10000L) - .expire("WORD_IMAGE_CLICK", 60000L) - // 设置拦截器 + .expire(CaptchaTypeConstant.WORD_IMAGE_CLICK, 60000L) + // 设置拦截器,默认是 EmptyCaptchaInterceptor.INSTANCE .setInterceptor(EmptyCaptchaInterceptor.INSTANCE) // 添加验证码背景图片 - .addResource("SLIDER", new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) - .addResource("WORD_IMAGE_CLICK", new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) - .addResource("ROTATE", new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) + // arg1 验证码类型(SLIDER、WORD_IMAGE_CLICK、ROTATE、CONCAT), + // arg2 验证码背景图片资源 + .addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) + .addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) + .addResource(CaptchaTypeConstant.ROTATE, new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) + .addResource(CaptchaTypeConstant.CONCAT, new Resource("classpath", "META-INF/cut-image/resource/1.jpg")) // 添加验证码模板图片 // .addTemplate("SLIDER",template1) - // 设置缓冲器,可提前生成验证码,用于增加并发性 + // 设置缓存器,可提前生成验证码,用于增加并发性,可以不设置,默认是不开启缓存 .cached(10, 1000, 5000, 10000L) // 添加字体包,用于给文字点选验证码提供字体 .addFont(new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\captcha\\手写字体\\ttf\\千图小兔体.ttf")) // 设置缓存存储器,如果要支持分布式,需要把这里改成分布式缓存,比如通过redis实现的 CacheStore 缓存 .setCacheStore(new LocalCacheStore()) - // 设置资源存储器,如果想在分布式环境或者想统一管理以及扩展 实现 ResourceStore 接口,自定义 -// .setResourceStore(new LocalMemoryResourceStore()) // 图片转换器,默认是将图片转换成base64格式, 背景图为jpg, 模板图为png, 如果想要扩展,可替换成自己实现的 .setTransform(new Base64ImageTransform()) .build(); - while (true){ + while (true) { long start = System.currentTimeMillis(); - ApiResponse response = application.generateCaptcha("SLIDER"); + + // 生成验证码 + // arg1 验证码类型(SLIDER、WORD_IMAGE_CLICK、ROTATE、CONCAT) + // response 为生成的验证码数据,可直接返回给前端 + ApiResponse response = application.generateCaptcha(CaptchaTypeConstant.SLIDER); + + + // 校验验证码 + // captchaId 和 track 数据应该是前端传来的验证数据 + String captchaId = ""; + ImageCaptchaTrack track = null; + ApiResponse matching = application.matching(captchaId, track); + + + if (matching.isSuccess()) { + System.out.println("校验成功"); + } else { + System.out.println("校验失败:" + matching.getMsg()); + } + System.out.println("耗时:" + (System.currentTimeMillis() - start)); // System.out.println(response); Thread.sleep(1000);