feat: 升级 2.0.0-SNAPSHOT (Java 21/Spring Boot 3.4.5) + 旋转验证码修复 + 新增验证码类型支持
This commit is contained in:
@@ -12,26 +12,27 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>tianai-captcha</module>
|
<module>tianai-captcha</module>
|
||||||
<module>tianai-captcha-springboot-starter</module>
|
<module>tianai-captcha-springboot-starter</module>
|
||||||
|
<module>tianai-captcha-platform</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.5.5</revision>
|
<revision>2.0.0-SNAPSHOT</revision>
|
||||||
<java.version>1.8</java.version>
|
<java.version>21</java.version>
|
||||||
<!-- 打包跳过单元测试 -->
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<maven.compiler.release>21</maven.compiler.release>
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
|
||||||
<skip.nexus>false</skip.nexus>
|
<spring-boot.version>3.4.5</spring-boot.version>
|
||||||
<serverId>ossrh</serverId>
|
<lombok.version>1.18.42</lombok.version>
|
||||||
<deplay.repository>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</deplay.repository>
|
<slf4j.version>2.0.17</slf4j.version>
|
||||||
<deplay.snapshotRepository>https://s01.oss.sonatype.org/content/repositories/snapshots</deplay.snapshotRepository>
|
<gson.version>2.13.1</gson.version>
|
||||||
|
<onnx-runtime.version>1.22.0</onnx-runtime.version>
|
||||||
|
|
||||||
<!-- 私服 -->
|
<skip.nexus>true</skip.nexus>
|
||||||
<!-- <skip.nexus>true</skip.nexus>-->
|
<serverId>ossrh</serverId>
|
||||||
<!-- <deplay.id>smart</deplay.id>-->
|
|
||||||
<!-- <deplay.repository>http://192.168.3.10:6061/repository/smart_hosted/</deplay.repository>-->
|
|
||||||
<!-- <deplay.snapshotRepository>http://192.168.3.10:6061/repository/smart_hosted/</deplay.snapshotRepository>-->
|
|
||||||
</properties>
|
</properties>
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
@@ -68,11 +69,10 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<!-- 统一版本号管理 -->
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>flatten-maven-plugin</artifactId>
|
<artifactId>flatten-maven-plugin</artifactId>
|
||||||
<version>1.2.7</version>
|
<version>1.7.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<updatePomFile>true</updatePomFile>
|
<updatePomFile>true</updatePomFile>
|
||||||
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
||||||
@@ -96,75 +96,23 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>2.9.1</version>
|
<version>3.14.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!-- 统一生成聚合文档,解决 mvn package 时控制台发出 javadoc 警告的问题 -->
|
<release>21</release>
|
||||||
<aggregate>true</aggregate>
|
<compilerArgument>-parameters</compilerArgument>
|
||||||
<failOnError>false</failOnError>
|
<annotationProcessorPaths>
|
||||||
<detectLinks>false</detectLinks>
|
<path>
|
||||||
</configuration>
|
<groupId>org.projectlombok</groupId>
|
||||||
<executions>
|
<artifactId>lombok</artifactId>
|
||||||
<execution>
|
<version>${lombok.version}</version>
|
||||||
<id>attach-javadocs</id>
|
</path>
|
||||||
<goals>
|
</annotationProcessorPaths>
|
||||||
<goal>jar</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<additionalparam>-Xdoclint:none</additionalparam>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
|
||||||
<version>2.2.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>attach-sources</id>
|
|
||||||
<goals>
|
|
||||||
<goal>jar-no-fork</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
|
||||||
<version>1.5</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>sign-artifacts</id>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>sign</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
|
||||||
<version>2.6</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.sonatype.central</groupId>
|
|
||||||
<artifactId>central-publishing-maven-plugin</artifactId>
|
|
||||||
<version>0.5.0</version>
|
|
||||||
<extensions>true</extensions>
|
|
||||||
<configuration>
|
|
||||||
<publishingServerId>ossrh</publishingServerId>
|
|
||||||
<!-- <tokenAuth>true</tokenAuth>-->
|
|
||||||
<autoPublish>true</autoPublish>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
<pluginManagement>
|
<pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-dependencies</artifactId>
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
<version>2.2.7.RELEASE</version>
|
<version>${spring-boot.version}</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -25,9 +25,7 @@
|
|||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>21</java.version>
|
||||||
<commons-lang3.version>3.7</commons-lang3.version>
|
|
||||||
<!-- 打包跳过单元测试 -->
|
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
@@ -43,7 +41,6 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>${commons-lang3.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
@@ -65,18 +62,14 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<optional>true</optional>
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cloud.tianai.captcha</groupId>
|
<groupId>cloud.tianai.captcha</groupId>
|
||||||
<artifactId>tianai-captcha</artifactId>
|
<artifactId>tianai-captcha</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>jakarta.validation</groupId>-->
|
|
||||||
<!-- <artifactId>jakarta.validation-api</artifactId>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
@@ -87,20 +80,22 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.14.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>8</source>
|
<release>21</release>
|
||||||
<target>8</target>
|
|
||||||
<compilerArgument>-parameters</compilerArgument>
|
<compilerArgument>-parameters</compilerArgument>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+13
-8
@@ -13,25 +13,23 @@
|
|||||||
<url>https://gitee.com/tianai/tianai-captcha</url>
|
<url>https://gitee.com/tianai/tianai-captcha</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>21</java.version>
|
||||||
<!-- 打包跳过单元测试 -->
|
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.42</version>
|
<version>${lombok.version}</version>
|
||||||
<optional>true</optional>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>1.7.30</version>
|
<version>${slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@@ -40,10 +38,17 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.14.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>8</source>
|
<release>21</release>
|
||||||
<target>8</target>
|
|
||||||
<compilerArgument>-parameters</compilerArgument>
|
<compilerArgument>-parameters</compilerArgument>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|||||||
+3
@@ -167,6 +167,9 @@ public class DefaultImageCaptchaApplication implements ImageCaptchaApplication {
|
|||||||
verificationVO.setBackgroundImageHeight(imageCaptchaInfo.getBackgroundImageHeight());
|
verificationVO.setBackgroundImageHeight(imageCaptchaInfo.getBackgroundImageHeight());
|
||||||
verificationVO.setTemplateImageWidth(imageCaptchaInfo.getTemplateImageWidth());
|
verificationVO.setTemplateImageWidth(imageCaptchaInfo.getTemplateImageWidth());
|
||||||
verificationVO.setTemplateImageHeight(imageCaptchaInfo.getTemplateImageHeight());
|
verificationVO.setTemplateImageHeight(imageCaptchaInfo.getTemplateImageHeight());
|
||||||
|
if (imageCaptchaInfo instanceof cloud.tianai.captcha.generator.common.model.dto.RotateImageCaptchaInfo) {
|
||||||
|
verificationVO.setDegree(((cloud.tianai.captcha.generator.common.model.dto.RotateImageCaptchaInfo) imageCaptchaInfo).getDegree());
|
||||||
|
}
|
||||||
verificationVO.setData(imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getViewData());
|
verificationVO.setData(imageCaptchaInfo.getData() == null ? null : imageCaptchaInfo.getData().getViewData());
|
||||||
verificationVO.setId(id);
|
verificationVO.setId(id);
|
||||||
return ApiResponse.ofSuccess(verificationVO);
|
return ApiResponse.ofSuccess(verificationVO);
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ public class ImageCaptchaVO implements Serializable {
|
|||||||
private Integer templateImageWidth;
|
private Integer templateImageWidth;
|
||||||
/** 滑动图片高度.*/
|
/** 滑动图片高度.*/
|
||||||
private Integer templateImageHeight;
|
private Integer templateImageHeight;
|
||||||
|
/** 旋转验证码初始角度(仅ROTATE类型).*/
|
||||||
|
private Double degree;
|
||||||
/** data 扩展数据.*/
|
/** data 扩展数据.*/
|
||||||
private Object data;
|
private Object data;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-9
@@ -1,18 +1,20 @@
|
|||||||
package cloud.tianai.captcha.common.constant;
|
package cloud.tianai.captcha.common.constant;
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author: 天爱有情
|
|
||||||
* @date 2021/8/7 17:14
|
|
||||||
* @Description 滑块类型
|
|
||||||
*/
|
|
||||||
public interface CaptchaTypeConstant {
|
public interface CaptchaTypeConstant {
|
||||||
|
|
||||||
/** 滑块. */
|
|
||||||
String SLIDER = "SLIDER";
|
String SLIDER = "SLIDER";
|
||||||
/** 旋转. */
|
String SLIDER_V2 = "SLIDER_V2";
|
||||||
String ROTATE = "ROTATE";
|
String ROTATE = "ROTATE";
|
||||||
/** 拼接. */
|
|
||||||
String CONCAT = "CONCAT";
|
String CONCAT = "CONCAT";
|
||||||
/** 文字图片点选. */
|
|
||||||
String WORD_IMAGE_CLICK = "WORD_IMAGE_CLICK";
|
String WORD_IMAGE_CLICK = "WORD_IMAGE_CLICK";
|
||||||
|
String ICON_CLICK = "ICON_CLICK";
|
||||||
|
String WORD_ORDER_CLICK = "WORD_ORDER_CLICK";
|
||||||
|
String CURVE_SLIDER = "CURVE_SLIDER";
|
||||||
|
String CURVE_SLIDER_V2 = "CURVE_SLIDER_V2";
|
||||||
|
String CURVE_SLIDER_V3 = "CURVE_SLIDER_V3";
|
||||||
|
String ANGLE = "ANGLE";
|
||||||
|
String SCRATCH = "SCRATCH";
|
||||||
|
String JIGSAW = "JIGSAW";
|
||||||
|
String CURVE_DRAW = "CURVE_DRAW";
|
||||||
|
String PROOF_OF_WORK = "PROOF_OF_WORK";
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -47,14 +47,21 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doInit() {
|
protected void doInit() {
|
||||||
// 滑块验证码
|
|
||||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(SLIDER, StandardSliderImageCaptchaGenerator::new));
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(SLIDER, StandardSliderImageCaptchaGenerator::new));
|
||||||
// 旋转验证码
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(SLIDER_V2, EnhancedSliderImageCaptchaGenerator::new));
|
||||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(ROTATE, StandardRotateImageCaptchaGenerator::new));
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(ROTATE, StandardRotateImageCaptchaGenerator::new));
|
||||||
// 拼接验证码
|
|
||||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new));
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CONCAT, StandardConcatImageCaptchaGenerator::new));
|
||||||
// 点选文字验证码
|
|
||||||
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, StandardWordClickImageCaptchaGenerator::new));
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_IMAGE_CLICK, StandardWordClickImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(ICON_CLICK, IconClickImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(WORD_ORDER_CLICK, WordOrderClickImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CURVE_SLIDER, CurveSliderImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(ANGLE, AngleRotateImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(SCRATCH, ScratchImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(JIGSAW, JigsawImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CURVE_SLIDER_V2, CurveSliderV2ImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CURVE_SLIDER_V3, CurveSliderV3ImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(CURVE_DRAW, CurveDrawImageCaptchaGenerator::new));
|
||||||
|
addImageCaptchaGeneratorProvider(new CommonImageCaptchaGeneratorProvider(PROOF_OF_WORK, ProofOfWorkCaptchaGenerator::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
|
public void addImageCaptchaGeneratorProvider(ImageCaptchaGeneratorProvider provider) {
|
||||||
|
|||||||
+10
-13
@@ -60,6 +60,14 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
Optional<BufferedImage> maskTemplateOptional = getTemplateImageOfOptional(templateResource, TEMPLATE_MASK_IMAGE_NAME);
|
Optional<BufferedImage> maskTemplateOptional = getTemplateImageOfOptional(templateResource, TEMPLATE_MASK_IMAGE_NAME);
|
||||||
if (maskTemplateOptional.isPresent()) {
|
if (maskTemplateOptional.isPresent()) {
|
||||||
maskTemplate = maskTemplateOptional.get();
|
maskTemplate = maskTemplateOptional.get();
|
||||||
|
} else {
|
||||||
|
// 没有mask.png时,生成实心圆形蒙版(alpha=255),确保切出完整图片内容
|
||||||
|
maskTemplate = new BufferedImage(fixedTemplate.getWidth(), fixedTemplate.getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB);
|
||||||
|
java.awt.Graphics2D gMask = maskTemplate.createGraphics();
|
||||||
|
gMask.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
gMask.setColor(new java.awt.Color(255, 255, 255, 255));
|
||||||
|
gMask.fillOval(0, 0, fixedTemplate.getWidth(), fixedTemplate.getHeight());
|
||||||
|
gMask.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 算出居中的x和y
|
// 算出居中的x和y
|
||||||
@@ -68,21 +76,10 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
|
|
||||||
// 抠图部分
|
// 抠图部分
|
||||||
BufferedImage cutImage = CaptchaImageUtils.cutImage(background, maskTemplate, x, y);
|
BufferedImage cutImage = CaptchaImageUtils.cutImage(background, maskTemplate, x, y);
|
||||||
BufferedImage rotateFixed = fixedTemplate;
|
// 随机x,转换为角度
|
||||||
BufferedImage rotateActive = activeTemplate;
|
|
||||||
if (param.getObfuscate()) {
|
|
||||||
int randomDegree = randomInt(10, 350);
|
|
||||||
rotateFixed = CaptchaImageUtils.rotateImage(fixedTemplate, randomDegree);
|
|
||||||
randomDegree = randomInt(10, 350);
|
|
||||||
rotateActive = CaptchaImageUtils.rotateImage(activeTemplate, randomDegree);
|
|
||||||
}
|
|
||||||
CaptchaImageUtils.overlayImage(background, rotateFixed, x, y);
|
|
||||||
CaptchaImageUtils.overlayImage(cutImage, rotateActive, 0, 0);
|
|
||||||
// 随机旋转抠图部分
|
|
||||||
// 随机x, 转换为角度
|
|
||||||
int randomX = randomInt(fixedTemplate.getWidth() + 10, background.getWidth() - 10);
|
int randomX = randomInt(fixedTemplate.getWidth() + 10, background.getWidth() - 10);
|
||||||
double degree = 360d - randomX / ((background.getWidth()) / 360d);
|
double degree = 360d - randomX / ((background.getWidth()) / 360d);
|
||||||
// 旋转的透明图片是一张正方形的
|
// 模板图:窄条透明画布,切片居中旋转放置(官方方式)
|
||||||
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(cutImage.getWidth(), background.getHeight());
|
BufferedImage matrixTemplate = CaptchaImageUtils.createTransparentImage(cutImage.getWidth(), background.getHeight());
|
||||||
CaptchaImageUtils.centerOverlayAndRotateImage(matrixTemplate, cutImage, degree);
|
CaptchaImageUtils.centerOverlayAndRotateImage(matrixTemplate, cutImage, degree);
|
||||||
|
|
||||||
|
|||||||
+149
-1
@@ -53,6 +53,9 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.ROTATE);
|
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.ROTATE);
|
||||||
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.SLIDER);
|
CaptchaTypeClassifier.addSliderCaptchaType(CaptchaTypeConstant.SLIDER);
|
||||||
CaptchaTypeClassifier.addClickCaptchaType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
|
CaptchaTypeClassifier.addClickCaptchaType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
|
||||||
|
CaptchaTypeClassifier.addClickCaptchaType(CaptchaTypeConstant.ICON_CLICK);
|
||||||
|
CaptchaTypeClassifier.addClickCaptchaType(CaptchaTypeConstant.WORD_ORDER_CLICK);
|
||||||
|
CaptchaTypeClassifier.addJigsawCaptchaType(CaptchaTypeConstant.JIGSAW);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleImageCaptchaValidator(float defaultTolerant) {
|
public SimpleImageCaptchaValidator(float defaultTolerant) {
|
||||||
@@ -119,6 +122,25 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
|
if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
|
||||||
// 滑动验证码
|
// 滑动验证码
|
||||||
addPercentage(imageCaptchaInfo, map);
|
addPercentage(imageCaptchaInfo, map);
|
||||||
|
} else if (CaptchaTypeConstant.CURVE_DRAW.equals(type)) {
|
||||||
|
// 曲线绘制验证码
|
||||||
|
if (expand == null) {
|
||||||
|
throw new IllegalArgumentException("曲线绘制验证码扩展数据为空, info=" + imageCaptchaInfo);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cloud.tianai.captcha.generator.impl.CurveDrawImageCaptchaGenerator.CurveDrawData curveData =
|
||||||
|
(cloud.tianai.captcha.generator.impl.CurveDrawImageCaptchaGenerator.CurveDrawData) expand;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (java.awt.geom.Point2D.Double point : curveData.getSamplePoints()) {
|
||||||
|
sb.append(calcPercentage((float) point.x, imageCaptchaInfo.getBackgroundImageWidth()))
|
||||||
|
.append(",")
|
||||||
|
.append(calcPercentage((float) point.y, imageCaptchaInfo.getBackgroundImageHeight()))
|
||||||
|
.append(";");
|
||||||
|
}
|
||||||
|
map.put(PERCENTAGE_KEY, sb.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("曲线绘制验证码扩展数据转换失败, info=" + imageCaptchaInfo, e);
|
||||||
|
}
|
||||||
} else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
|
} else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
|
||||||
// 图片点选验证码
|
// 图片点选验证码
|
||||||
if (expand == null) {
|
if (expand == null) {
|
||||||
@@ -158,6 +180,15 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
|
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
|
||||||
// 拼图验证码
|
// 拼图验证码
|
||||||
map.put(PERCENTAGE_KEY, expand);
|
map.put(PERCENTAGE_KEY, expand);
|
||||||
|
} else if (CaptchaTypeConstant.SCRATCH.equals(type)) {
|
||||||
|
// 刮刮卡验证码 - 存储需要刮开的百分比阈值
|
||||||
|
String threshold = (expand != null) ? String.valueOf(expand) : "50";
|
||||||
|
map.put(PERCENTAGE_KEY, threshold);
|
||||||
|
} else if (CaptchaTypeConstant.PROOF_OF_WORK.equals(type)) {
|
||||||
|
// 工作量证明验证码 - 存储challenge和difficulty
|
||||||
|
if (expand != null) {
|
||||||
|
map.put("pow_data", expand);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,12 +267,21 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
|
if (CaptchaTypeClassifier.isSliderCaptcha(type)) {
|
||||||
// 滑动类型验证码
|
// 滑动类型验证码
|
||||||
return doValidSliderCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
return doValidSliderCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||||
|
} else if (CaptchaTypeConstant.CURVE_DRAW.equals(type)) {
|
||||||
|
// 曲线绘制验证码
|
||||||
|
return doValidCurveDrawCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant);
|
||||||
} else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
|
} else if (CaptchaTypeClassifier.isClickCaptcha(type)) {
|
||||||
// 点选类型验证码
|
// 点选类型验证码
|
||||||
return doValidClickCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
return doValidClickCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||||
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
|
} else if (CaptchaTypeClassifier.isJigsawCaptcha(type)) {
|
||||||
// 拼图类型验证码
|
// 拼图类型验证码
|
||||||
return doValidJigsawCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
return doValidJigsawCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||||
|
} else if (CaptchaTypeConstant.SCRATCH.equals(type)) {
|
||||||
|
// 刮刮卡验证码
|
||||||
|
return doValidScratchCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant);
|
||||||
|
} else if (CaptchaTypeConstant.PROOF_OF_WORK.equals(type)) {
|
||||||
|
// 工作量证明验证码
|
||||||
|
return doValidProofOfWork(imageCaptchaTrack, imageCaptchaValidData);
|
||||||
}
|
}
|
||||||
// 不支持的类型
|
// 不支持的类型
|
||||||
log.warn("校验验证码警告, 不支持的验证码类型:{}, 请手动扩展 cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator.doValid 进行校验扩展", type);
|
log.warn("校验验证码警告, 不支持的验证码类型:{}, 请手动扩展 cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator.doValid 进行校验扩展", type);
|
||||||
@@ -254,7 +294,115 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, Slide
|
|||||||
}
|
}
|
||||||
String posArr = (String) imageCaptchaTrack.getData();
|
String posArr = (String) imageCaptchaTrack.getData();
|
||||||
String successPosStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, null);
|
String successPosStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, null);
|
||||||
return successPosStr.equals(posArr);
|
return successPosStr != null && successPosStr.equals(posArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验刮刮卡验证码
|
||||||
|
* 检查用户刮开的面积是否超过阈值
|
||||||
|
*/
|
||||||
|
public boolean doValidScratchCaptcha(ImageCaptchaTrack imageCaptchaTrack,
|
||||||
|
AnyMap imageCaptchaValidData,
|
||||||
|
Float tolerant) {
|
||||||
|
// data 字段存储刮开百分比 (0-100)
|
||||||
|
if (imageCaptchaTrack.getData() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int scratchPercent;
|
||||||
|
try {
|
||||||
|
scratchPercent = Integer.parseInt(String.valueOf(imageCaptchaTrack.getData()));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 阈值默认50%
|
||||||
|
String thresholdStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, "50");
|
||||||
|
int threshold = 50;
|
||||||
|
try {
|
||||||
|
threshold = Integer.parseInt(thresholdStr);
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
return scratchPercent >= threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验工作量证明验证码
|
||||||
|
* 检查用户提交的nonce是否满足SHA-256前导零要求
|
||||||
|
*/
|
||||||
|
public boolean doValidProofOfWork(ImageCaptchaTrack imageCaptchaTrack,
|
||||||
|
AnyMap imageCaptchaValidData) {
|
||||||
|
if (imageCaptchaTrack.getData() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String nonce = String.valueOf(imageCaptchaTrack.getData());
|
||||||
|
Object powData = imageCaptchaValidData.get("pow_data");
|
||||||
|
if (powData == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// pow_data is the PoWData object from the generator
|
||||||
|
try {
|
||||||
|
cloud.tianai.captcha.generator.impl.ProofOfWorkCaptchaGenerator.PoWData data =
|
||||||
|
(cloud.tianai.captcha.generator.impl.ProofOfWorkCaptchaGenerator.PoWData) powData;
|
||||||
|
return cloud.tianai.captcha.generator.impl.ProofOfWorkCaptchaGenerator.verifyProof(
|
||||||
|
data.challenge, data.difficulty, nonce);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验曲线绘制验证码
|
||||||
|
* 检查用户绘制的路径是否经过预期曲线的采样点附近
|
||||||
|
*/
|
||||||
|
public boolean doValidCurveDrawCaptcha(ImageCaptchaTrack imageCaptchaTrack,
|
||||||
|
AnyMap imageCaptchaValidData,
|
||||||
|
Float tolerant) {
|
||||||
|
String validStr = imageCaptchaValidData.getString(PERCENTAGE_KEY, null);
|
||||||
|
if (ObjectUtils.isEmpty(validStr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String[] splitArr = validStr.split(";");
|
||||||
|
List<ImageCaptchaTrack.Track> trackList = imageCaptchaTrack.getTrackList();
|
||||||
|
if (trackList == null || trackList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 取出所有 MOVE 类型的轨迹点(用户的绘制路径)
|
||||||
|
List<ImageCaptchaTrack.Track> drawTrackList = trackList
|
||||||
|
.stream()
|
||||||
|
.filter(t -> TrackTypeConstant.MOVE.equalsIgnoreCase(t.getType()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (drawTrackList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 对每个预期采样点,检查用户路径中是否有接近的点
|
||||||
|
int matchedCount = 0;
|
||||||
|
int bgWidth = imageCaptchaTrack.getBgImageWidth();
|
||||||
|
int bgHeight = imageCaptchaTrack.getBgImageHeight();
|
||||||
|
for (String posStr : splitArr) {
|
||||||
|
if (ObjectUtils.isEmpty(posStr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] posArr = posStr.split(",");
|
||||||
|
if (posArr.length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float xPercentage = Float.parseFloat(posArr[0]);
|
||||||
|
float yPercentage = Float.parseFloat(posArr[1]);
|
||||||
|
// 检查用户路径中是否有接近该采样点的位置
|
||||||
|
boolean matched = false;
|
||||||
|
for (ImageCaptchaTrack.Track track : drawTrackList) {
|
||||||
|
float calcXPercentage = calcPercentage(track.getX(), bgWidth);
|
||||||
|
float calcYPercentage = calcPercentage(track.getY(), bgHeight);
|
||||||
|
if (checkPercentage(calcXPercentage, xPercentage, tolerant)
|
||||||
|
&& checkPercentage(calcYPercentage, yPercentage, tolerant)) {
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
|
matchedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果匹配率超过 70% 则通过
|
||||||
|
return (double) matchedCount / splitArr.length >= 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 5.1 KiB |
Reference in New Issue
Block a user