旋转图片

This commit is contained in:
天爱有情
2022-04-22 17:44:27 +08:00
parent f7abf83f0f
commit ebe6e23e0a
22 changed files with 676 additions and 353 deletions
@@ -176,4 +176,43 @@ public class CaptchaImageUtils {
}
// /**
// * 旋转图片
// *
// * @param bufferedImage 图片
// * @param degree 旋转xx度
// */
// public static void rotateImage(BufferedImage bufferedImage, int degree) {
// // 创建Graphics2D对象,用在底图对象上绘图
// Graphics2D g2d = bufferedImage.createGraphics();
// // 绘制
// g2d.rotate(Math.toRadians(degree), bufferedImage.getWidth() / 2, bufferedImage.getHeight()/2);
// g2d.drawImage(bufferedImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null);
// // 释放图形上下文使用的系统资源
// g2d.dispose();
// }
public static BufferedImage rotateImage(final BufferedImage bufferedimage,
final double degree) {
// 得到图片宽度。
int w = bufferedimage.getWidth();
// 得到图片高度。
int h = bufferedimage.getHeight();
// 得到图片透明度。
int type = bufferedimage.getColorModel().getTransparency();
BufferedImage img;// 空的图片。
Graphics2D graphics2d;// 空的画笔。
(graphics2d = (img = new BufferedImage(w, h, type))
.createGraphics()).setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// 旋转,degree是整型,度数,比如垂直90度。
graphics2d.rotate(Math.toRadians(degree), w / 2, h / 2);
// 从bufferedimagecopy图片至img0,0是img的坐标。
graphics2d.drawImage(bufferedimage, 0, 0, null);
graphics2d.dispose();
// 返回复制好的图片,原图片依然没有变,没有旋转,下次还可以使用。
return img;
}
}
@@ -0,0 +1,77 @@
package cloud.tianai.captcha.template.slider.generator;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: 天爱有情
* @date 2022/4/22 16:30
* @Description 抽象的验证码生成器
*/
public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGenerator {
public static String DEFAULT_BG_IMAGE_TYPE = "jpeg";
public static String DEFAULT_SLIDER_IMAGE_TYPE = "png";
@Getter
@Setter
/** 默认背景图片类型. */
public String defaultBgImageType = DEFAULT_BG_IMAGE_TYPE;
@Getter
@Setter
/** 默认滑块图片类型. */
public String defaultSliderImageType = DEFAULT_SLIDER_IMAGE_TYPE;
@Override
public ImageCaptchaInfo generateCaptchaImage(String type) {
return generateCaptchaImage(type, defaultBgImageType, defaultSliderImageType);
}
@SneakyThrows
@Override
public ImageCaptchaInfo generateCaptchaImage(String type, String backgroundFormatName, String sliderFormatName) {
return generateCaptchaImage(GenerateParam.builder()
.type(type)
.backgroundFormatName(backgroundFormatName)
.sliderFormatName(sliderFormatName)
.obfuscate(false)
.build());
}
/**
* 将图片转换成字符串格式
*
* @param bufferedImage 图片
* @param formatType 格式化类型
* @return String
*/
public String transform(BufferedImage bufferedImage, String formatType) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, formatType, byteArrayOutputStream);
//转换成字节码
byte[] data = byteArrayOutputStream.toByteArray();
String base64 = Base64.getEncoder().encodeToString(data);
return "data:image/" + formatType + ";base64,".concat(base64);
}
protected InputStream getTemplateFile(Map<String, Resource> templateImages, String imageName) {
Resource resource = templateImages.get(imageName);
if (resource == null) {
throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName));
}
return getSlideImageResourceManager().getResourceInputStream(resource);
}
}
@@ -1,33 +1,35 @@
package cloud.tianai.captcha.template.slider.generator;
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator;
/**
* @Author: 天爱有情
* @date 2020/10/19 18:37
* @Description 滑块验证码模板
* @Description 图片验证码生成器
*/
public interface SliderCaptchaGenerator {
public interface ImageCaptchaGenerator {
/**
* 获取滑块验证码
*
* 生成验证码图片
* @param type 类型 {@link CaptchaTypeConstant}
* @return SliderCaptchaInfo
*/
SliderCaptchaInfo generateSlideImageInfo();
ImageCaptchaInfo generateCaptchaImage(String type);
/**
* 生成滑块验证码
*
* @param type type {@link CaptchaTypeConstant}
* @param targetFormatName jpeg或者webp格式
* @param matrixFormatName png或者webp格式
* @return SliderCaptchaInfo
*/
SliderCaptchaInfo generateSlideImageInfo(String targetFormatName, String matrixFormatName);
ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName);
/**
* 生成滑块验证码
@@ -35,20 +37,8 @@ public interface SliderCaptchaGenerator {
* @param param 生成参数
* @return SliderCaptchaInfo
*/
SliderCaptchaInfo generateSlideImageInfo(GenerateParam param);
ImageCaptchaInfo generateCaptchaImage(GenerateParam param);
/**
* 百分比对比
*
* @param newPercentage 用户百分比
* @param oriPercentage 原百分比
* @return true 成功 false 失败
* <p>
* 废除 建议使用
* @see SliderCaptchaValidator 进行校验
*/
@Deprecated
boolean percentageContrast(Float newPercentage, Float oriPercentage);
/**
* 获取滑块验证码资源管理器
@@ -0,0 +1,14 @@
package cloud.tianai.captcha.template.slider.generator.common.constant;
/**
* @Author: 天爱有情
* @date 2021/8/7 17:14
* @Description 滑块类型
*/
public interface CaptchaTypeConstant {
/** 滑块. */
String SLIDER = "SLIDER";
/** 旋转. */
String ROTATE = "ROTATE";
}
@@ -21,4 +21,6 @@ public class GenerateParam {
private String sliderFormatName = "png";
/** 是否混淆.*/
private Boolean obfuscate = false;
/** 类型.*/
private String type;
}
@@ -0,0 +1,66 @@
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: 天爱有情
* @Date 2020/5/29 8:04
* @Description 滑块验证码
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ImageCaptchaInfo {
/**
* 背景图
*/
private String backgroundImage;
/**
* 移动图
*/
private String sliderImage;
/** 背景图片宽度. */
private Integer bgImageWidth;
/** 背景图片高度. */
private Integer bgImageHeight;
/** 滑块图片宽度. */
private Integer sliderImageWidth;
/** 滑块图片高度. */
private Integer sliderImageHeight;
/** 随机值.*/
private Integer randomX;
/**
* 扩展字段
*/
public Object expand;
public ImageCaptchaInfo(String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight,
Integer randomX) {
this.backgroundImage = backgroundImage;
this.sliderImage = sliderImage;
this.bgImageWidth = bgImageWidth;
this.bgImageHeight = bgImageHeight;
this.sliderImageWidth = sliderImageWidth;
this.sliderImageHeight = sliderImageHeight;
this.randomX = randomX;
}
public static ImageCaptchaInfo of(String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight,
Integer randomKey) {
return new ImageCaptchaInfo( backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey);
}
}
@@ -0,0 +1,43 @@
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @Author: 天爱有情
* @date 2022/4/22 15:49
* @Description 旋转图片
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RotateImageCaptchaInfo extends ImageCaptchaInfo {
/**
* 旋转多少度
*/
private Double degree;
public static RotateImageCaptchaInfo of(Double degree,
Integer randomX,
String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight) {
RotateImageCaptchaInfo rotateImageCaptchaInfo = new RotateImageCaptchaInfo();
rotateImageCaptchaInfo.setDegree(degree);
rotateImageCaptchaInfo.setRandomX(randomX);
rotateImageCaptchaInfo.setBackgroundImage(backgroundImage);
rotateImageCaptchaInfo.setSliderImage(sliderImage);
rotateImageCaptchaInfo.setBgImageWidth(bgImageWidth);
rotateImageCaptchaInfo.setBgImageHeight(bgImageHeight);
rotateImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
rotateImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
return rotateImageCaptchaInfo;
}
}
@@ -1,66 +0,0 @@
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @Author: 天爱有情
* @Date 2020/5/29 8:04
* @Description 滑块验证码
*/
@Data
@AllArgsConstructor
public class SliderCaptchaInfo {
/**
* x轴
*/
private Integer x;
/**
* y轴
*/
private Integer y;
/**
* 背景图
*/
private String backgroundImage;
/**
* 移动图
*/
private String sliderImage;
/** 背景图片宽度. */
private Integer bgImageWidth;
/** 背景图片高度. */
private Integer bgImageHeight;
/** 滑块图片宽度. */
private Integer sliderImageWidth;
/** 滑块图片高度. */
private Integer sliderImageHeight;
/**
* 扩展字段
*/
public Object expand;
public SliderCaptchaInfo(Integer x, Integer y, String backgroundImage, String sliderImage, Integer bgImageWidth, Integer bgImageHeight, Integer sliderImageWidth, Integer sliderImageHeight) {
this.x = x;
this.y = y;
this.backgroundImage = backgroundImage;
this.sliderImage = sliderImage;
this.bgImageWidth = bgImageWidth;
this.bgImageHeight = bgImageHeight;
this.sliderImageWidth = sliderImageWidth;
this.sliderImageHeight = sliderImageHeight;
}
public static SliderCaptchaInfo of(Integer x,
Integer y,
String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight) {
return new SliderCaptchaInfo(x, y, backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight);
}
}
@@ -0,0 +1,43 @@
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SliderImageCaptchaInfo extends ImageCaptchaInfo {
/**
* x轴
*/
private Integer x;
/**
* y轴
*/
private Integer y;
public static SliderImageCaptchaInfo of(Integer x,
Integer y,
String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight) {
SliderImageCaptchaInfo sliderImageCaptchaInfo = new SliderImageCaptchaInfo();
sliderImageCaptchaInfo.setX(x);
sliderImageCaptchaInfo.setY(y);
sliderImageCaptchaInfo.setRandomX(x);
sliderImageCaptchaInfo.setBackgroundImage(backgroundImage);
sliderImageCaptchaInfo.setSliderImage(sliderImage);
sliderImageCaptchaInfo.setBgImageWidth(bgImageWidth);
sliderImageCaptchaInfo.setBgImageHeight(bgImageHeight);
sliderImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
sliderImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
return sliderImageCaptchaInfo;
}
}
@@ -1,8 +1,8 @@
package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.common.util.NamedThreadFactory;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import lombok.Getter;
@@ -22,12 +22,12 @@ import java.util.concurrent.atomic.AtomicInteger;
* @Description 滑块验证码缓冲器
*/
@Slf4j
public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
protected final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("slider-captcha-queue"));
protected ConcurrentLinkedQueue<SliderCaptchaInfo> queue;
protected ConcurrentLinkedQueue<ImageCaptchaInfo> queue;
protected AtomicInteger pos = new AtomicInteger(0);
protected SliderCaptchaGenerator target;
protected ImageCaptchaGenerator target;
protected int size;
/** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */
protected int waitTime = 1000;
@@ -39,13 +39,13 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
@Setter
protected boolean requiredGetCaptcha = true;
public CacheSliderCaptchaGenerator(SliderCaptchaGenerator target, GenerateParam generateParam, int size) {
public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size) {
this.target = target;
this.generateParam = generateParam;
this.size = size;
}
public CacheSliderCaptchaGenerator(SliderCaptchaGenerator target, GenerateParam generateParam, int size, int waitTime, int period) {
public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size, int waitTime, int period) {
this.target = target;
this.generateParam = generateParam;
this.size = size;
@@ -71,7 +71,7 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
if (pos.get() >= size) {
return;
}
SliderCaptchaInfo slideImageInfo = target.generateSlideImageInfo(generateParam);
ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(generateParam);
if (slideImageInfo != null) {
boolean addStatus = queue.offer(slideImageInfo);
if (addStatus) {
@@ -101,17 +101,17 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
@SneakyThrows
@Override
public SliderCaptchaInfo generateSlideImageInfo() {
return generateSlideImageInfo(this.requiredGetCaptcha);
public ImageCaptchaInfo generateCaptchaImage(String type) {
return generateCaptchaImage(this.requiredGetCaptcha);
}
@SneakyThrows
public SliderCaptchaInfo generateSlideImageInfo(boolean requiredGetCaptcha) {
SliderCaptchaInfo poll = queue.poll();
public ImageCaptchaInfo generateCaptchaImage(boolean requiredGetCaptcha) {
ImageCaptchaInfo poll = queue.poll();
if (poll == null && requiredGetCaptcha) {
log.warn("滑块验证码缓存不足, genParam:{}", generateParam);
// 如果池内没数据 则直接生成
return target.generateSlideImageInfo(generateParam);
return target.generateCaptchaImage(generateParam);
}
// 减1
pos.decrementAndGet();
@@ -119,18 +119,13 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
}
@Override
public SliderCaptchaInfo generateSlideImageInfo(String targetFormatName, String matrixFormatName) {
return target.generateSlideImageInfo(targetFormatName, matrixFormatName);
public ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName) {
return target.generateCaptchaImage(type,targetFormatName, matrixFormatName);
}
@Override
public SliderCaptchaInfo generateSlideImageInfo(GenerateParam param) {
return target.generateSlideImageInfo(param);
}
@Override
public boolean percentageContrast(Float newPercentage, Float oriPercentage) {
return target.percentageContrast(newPercentage, oriPercentage);
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
return target.generateCaptchaImage(param);
}
@Override
@@ -1,24 +1,25 @@
package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import lombok.Getter;
import lombok.Setter;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.*;
@@ -29,7 +30,7 @@ import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils
* @Description 滑块验证码模板
*/
@Slf4j
public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/**
* 默认的resource资源文件路径.
@@ -40,59 +41,28 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
*/
public static final String DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH = "META-INF/cut-image/template";
public static String DEFAULT_BG_IMAGE_TYPE = "jpeg";
public static String DEFAULT_SLIDER_IMAGE_TYPE = "png";
public static float DEFAULT_TOLERANT = 0.02f;
protected final SliderCaptchaResourceManager sliderCaptchaResourceManager;
@Getter
@Setter
/** 容错值. */
public float tolerant = DEFAULT_TOLERANT;
@Getter
@Setter
/** 默认背景图片类型. */
public String defaultBgImageType = DEFAULT_BG_IMAGE_TYPE;
@Getter
@Setter
/** 默认滑块图片类型. */
public String defaultSliderImageType = DEFAULT_SLIDER_IMAGE_TYPE;
public StandardSliderCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager,
boolean initDefaultResource) {
public StandardImageCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager,
boolean initDefaultResource) {
this.sliderCaptchaResourceManager = sliderCaptchaResourceManager;
if (initDefaultResource) {
initDefaultResource();
}
}
@Override
public SliderCaptchaInfo generateSlideImageInfo() {
return generateSlideImageInfo(defaultBgImageType, defaultSliderImageType);
}
@SneakyThrows
@Override
public SliderCaptchaInfo generateSlideImageInfo(String backgroundFormatName, String sliderFormatName) {
return generateSlideImageInfo(GenerateParam.builder()
.backgroundFormatName(backgroundFormatName)
.sliderFormatName(sliderFormatName)
.obfuscate(false)
.build());
}
@SneakyThrows
@Override
public SliderCaptchaInfo generateSlideImageInfo(GenerateParam param) {
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
Boolean obfuscate = param.getObfuscate();
Map<String, Resource> templateImages = sliderCaptchaResourceManager.randomGetTemplate();
Map<String, Resource> templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType());
if (templateImages == null || templateImages.isEmpty()) {
return null;
}
Collection<InputStream> inputStreams = new LinkedList<>();
try {
Resource resourceImage = sliderCaptchaResourceManager.randomGetResource();
Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(param.getType());
InputStream resourceInputStream = sliderCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream);
BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream);
@@ -151,16 +121,16 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
* @return SliderCaptchaInfo
*/
@SneakyThrows
public SliderCaptchaInfo wrapSliderCaptchaInfo(int randomX,
int randomY,
BufferedImage backgroundImage,
BufferedImage sliderImage,
GenerateParam param) {
public SliderImageCaptchaInfo wrapSliderCaptchaInfo(int randomX,
int randomY,
BufferedImage backgroundImage,
BufferedImage sliderImage,
GenerateParam param) {
String backgroundFormatName = param.getBackgroundFormatName();
String sliderFormatName = param.getSliderFormatName();
String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName);
String sliderImageBase64 = transform(sliderImage, sliderFormatName);
return SliderCaptchaInfo.of(randomX, randomY,
return SliderImageCaptchaInfo.of(randomX, randomY,
backGroundImageBase64,
sliderImageBase64,
backgroundImage.getWidth(), backgroundImage.getHeight(),
@@ -168,41 +138,6 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
);
}
/**
* 将图片转换成字符串格式
*
* @param bufferedImage 图片
* @param formatType 格式化类型
* @return String
*/
public String transform(BufferedImage bufferedImage, String formatType) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, formatType, byteArrayOutputStream);
//转换成字节码
byte[] data = byteArrayOutputStream.toByteArray();
String base64 = Base64.getEncoder().encodeToString(data);
return "data:image/" + formatType + ";base64,".concat(base64);
}
/**
* 百分比对比
*
* @param newPercentage 用户百分比
* @param oriPercentage 原百分比
* @return true 成功 false 失败
*/
@Override
public boolean percentageContrast(Float newPercentage, Float oriPercentage) {
if (newPercentage == null || Float.isNaN(newPercentage) || Float.isInfinite(newPercentage)
|| oriPercentage == null || Float.isNaN(oriPercentage) || Float.isInfinite(oriPercentage)) {
return false;
}
// 容错值
float maxTolerant = oriPercentage + tolerant;
float minTolerant = oriPercentage - tolerant;
return newPercentage >= minTolerant && newPercentage <= maxTolerant;
}
@Override
public SliderCaptchaResourceManager getSlideImageResourceManager() {
return sliderCaptchaResourceManager;
@@ -217,34 +152,26 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
return ThreadLocalRandom.current().nextInt(slWidth, sliderX - slWidth);
}
protected InputStream getTemplateFile(Map<String, Resource> templateImages, String imageName) {
Resource resource = templateImages.get(imageName);
if (resource == null) {
throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName));
}
return sliderCaptchaResourceManager.getResourceInputStream(resource);
}
/**
* 初始化默认资源
*/
public void initDefaultResource() {
ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore();
// 添加一些系统的资源文件
resourceStore.addResource(new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
// 添加一些系统的 模板文件
Map<String, Resource> template1 = new HashMap<>(4);
template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png")));
template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png")));
template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/matrix.png")));
resourceStore.addTemplate(template1);
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template1);
Map<String, Resource> template2 = new HashMap<>(4);
template2.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png")));
template2.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png")));
template2.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/matrix.png")));
resourceStore.addTemplate(template2);
resourceStore.addTemplate(CaptchaTypeConstant.SLIDER, template2);
}
}
@@ -0,0 +1,147 @@
package cloud.tianai.captcha.template.slider.generator.impl;
import cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.RotateImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.*;
import static cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
import static cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH;
/**
* @Author: 天爱有情
* @date 2022/4/22 16:43
* @Description 旋转图片验证码生成器
*/
public class StandardRotateCaptchaGenerator extends AbstractImageCaptchaGenerator {
protected final SliderCaptchaResourceManager sliderCaptchaResourceManager;
public StandardRotateCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager, boolean initDefaultResource) {
this.sliderCaptchaResourceManager = sliderCaptchaResourceManager;
if (initDefaultResource) {
initDefaultResource();
}
}
public void initDefaultResource() {
ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore();
// 添加一些系统的资源文件
resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
// 添加一些系统的 模板文件
Map<String, Resource> template1 = new HashMap<>(4);
template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png")));
template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template1);
}
@Override
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
// 旋转验证码没有混淆
Boolean obfuscate = param.getObfuscate();
Map<String, Resource> templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType());
if (templateImages == null || templateImages.isEmpty()) {
return null;
}
Collection<InputStream> inputStreams = new LinkedList<>();
try {
Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(param.getType());
InputStream resourceInputStream = sliderCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream);
BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream);
// 拷贝一份图片
BufferedImage targetBackground = deepCopyBufferedImage(cutBackground);
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
inputStreams.add(fixedTemplateInput);
BufferedImage fixedTemplate = wrapFile2BufferedImage(fixedTemplateInput);
InputStream activeTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME);
inputStreams.add(activeTemplateInput);
BufferedImage activeTemplate = wrapFile2BufferedImage(activeTemplateInput);
// 算出居中的x和y
int x = targetBackground.getWidth() / 2 - fixedTemplate.getWidth() / 2;
int y = targetBackground.getHeight() / 2 - fixedTemplate.getHeight() / 2;
overlayImage(targetBackground, fixedTemplate, x, y);
// 抠图部分
BufferedImage cutImage = cutImage(cutBackground, fixedTemplate, x, y);
overlayImage(cutImage, activeTemplate, 0, 0);
// 随机旋转抠图部分
// 随机x 转换为角度
int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 10, targetBackground.getWidth() - 10);
double degree = randomX / ((targetBackground.getWidth()) / 360d);
// int degree = ThreadLocalRandom.current().nextInt(10, 350);
cutImage = rotateImage(cutImage, degree);
return wrapRotateCaptchaInfo(degree, randomX, targetBackground, cutImage, param);
} finally {
// 使用完后关闭流
for (InputStream inputStream : inputStreams) {
try {
inputStream.close();
} catch (IOException e) {
// ignore
}
}
}
}
@SneakyThrows
private ImageCaptchaInfo wrapRotateCaptchaInfo(double degree, int randomX, BufferedImage backgroundImage, BufferedImage sliderImage, GenerateParam param) {
String backgroundFormatName = param.getBackgroundFormatName();
String sliderFormatName = param.getSliderFormatName();
String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName);
String sliderImageBase64 = transform(sliderImage, sliderFormatName);
return RotateImageCaptchaInfo.of(degree,
randomX,
backGroundImageBase64,
sliderImageBase64,
backgroundImage.getWidth(), backgroundImage.getHeight(),
sliderImage.getWidth(), sliderImage.getHeight()
);
}
@Override
public SliderCaptchaResourceManager getSlideImageResourceManager() {
return sliderCaptchaResourceManager;
}
public static void main(String[] args) throws IOException {
BufferedImage bgImage = CaptchaImageUtils.wrapFile2BufferedImage(
new FileInputStream("E:\\projects\\tianai-captcha\\src\\main\\resources\\META-INF\\cut-image\\resource\\1.jpg"));
BufferedImage image1 = CaptchaImageUtils.wrapFile2BufferedImage(new FileInputStream("C:\\Users\\Thinkpad\\Desktop\\a.png"));
BufferedImage image2 = CaptchaImageUtils.wrapFile2BufferedImage(new FileInputStream("C:\\Users\\Thinkpad\\Desktop\\b.png"));
BufferedImage cutImage = CaptchaImageUtils.cutImage(bgImage, image1, bgImage.getWidth() / 2 - 100, bgImage.getHeight() / 2 - 100);
CaptchaImageUtils.overlayImage(cutImage, image2, 0, 0);
cutImage = CaptchaImageUtils.rotateImage(cutImage, 180);
CaptchaImageUtils.overlayImage(bgImage, image1, bgImage.getWidth() / 2 - 100, bgImage.getHeight() / 2 - 100);
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\a1.jpg");
FileOutputStream file2OutputStream = new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\a2.png");
ImageIO.write(bgImage, "jpeg", fileOutputStream);
ImageIO.write(cutImage, "png", file2OutputStream);
fileOutputStream.close();
file2OutputStream.close();
}
}
@@ -12,97 +12,97 @@ public interface ResourceStore {
*
* @param resource 资源
*/
void addResource(Resource resource);
void addResource(String type, Resource resource);
/**
* 设置资源
* 清除某个类型下的所有资源
*
* @param resources resources
* @param type type
*/
void setResource(List<Resource> resources);
/**
* 删除资源
*
* @param resource resource
*/
boolean deleteResource(Resource resource);
void clearResources(String type);
/**
* 清除所有资源
*/
void clearResources();
/**
* 添加模板
*
* @param template template
*/
void addTemplate(Map<String, Resource> template);
/**
* 设置模板
*
* @param templateResource templateResource
*/
void setTemplates(List<Map<String, Resource>> templateResource);
/**
* 删除模板
*
* @param template template
*/
void deleteTemplate(Map<String, Resource> template);
/**
* 清除所有模板
*/
void clearTemplates();
void clearAllResources();
/**
* 获取所有资源对象
*
* @return List<Resource>
*/
List<Resource> listResources();
Map<String,List<Resource>> listAllResources();
/**
* 获取所有模板
* 获取某个type下的所有资源对象
*
* @return List<Map < String, Resource>>
* @param type type
* @return List<Resource>
*/
List<Map<String, Resource>> listTemplates();
List<Resource> listResourcesByType(String type);
/**
* 随机获取某个资源
*
* @param type type
* @return Resource
*/
Resource randomGetResource(String type);
/**
* 获取资源总数
*
* @return int
*/
int getResourceCount();
int getAllResourceCount();
/**
* 获取模板count
* 获取某个type下的资源总数
*
* @param type type
* @return int
*/
int getTemplateCount();
int getResourceCount(String type);
/**
* 获取资源通过index
* 添加模板
*
* @param index index
* @return Resource
* @param template template
*/
Resource getResourceByIndex(int index);
void addTemplate(String type, Map<String, Resource> template);
/**
* 获取模板通过indx
* 清除所有模板
*/
void clearAllTemplates();
/**
* 清除某个type下的所有模板
*
* @param index index
* @param type type
*/
void clearTemplates(String type);
/**
* 获取所有模板通过type
*
* @return List<Map < String, Resource>>
*/
List<Map<String, Resource>> listTemplatesByType(String type);
/**
* 获取所有模板
*
* @return Map<String, List < Map < String, Resource>>>
*/
Map<String, List<Map<String, Resource>>> listAllTemplates();
/**
* 随机获取某个模板通过type
*
* @param type type
* @return Map<String, Resource>
*/
Map<String, Resource> getTemplateByIndex(int index);
Map<String, Resource> randomGetTemplateByType(String type);
}
@@ -18,14 +18,14 @@ public interface SliderCaptchaResourceManager {
*
* @return Map<String, Resource>
*/
Map<String, Resource> randomGetTemplate();
Map<String, Resource> randomGetTemplate(String type);
/**
* 随机获取某个资源对象
*
* @return Resource
*/
Resource randomGetResource();
Resource randomGetResource(String type);
/**
* 获取真正的资源流通过资源对象
@@ -1,12 +1,11 @@
package cloud.tianai.captcha.template.slider.resource.impl;
import cloud.tianai.captcha.template.slider.common.util.CollectionUtils;
import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: 天爱有情
@@ -17,86 +16,103 @@ public class DefaultResourceStore implements ResourceStore {
/**
* 模板资源.
*/
private List<Map<String, Resource>> templateResourceList = new ArrayList<>(2);
private Map<String, List<Map<String, Resource>>> templateResourceMap = new HashMap<>(2);
/**
* resource.
*/
private List<Resource> resourceList = new ArrayList<>(20);
private Map<String, List<Resource>> resourceMap = new HashMap<>(2);
@Override
public void addResource(Resource resource) {
resourceList.add(resource);
public void addResource(String type, Resource resource) {
resourceMap.computeIfAbsent(type, k -> new ArrayList<>(20)).add(resource);
}
@Override
public void setResource(List<Resource> resources) {
resourceList = new ArrayList<>(resources);
public void clearResources(String type) {
resourceMap.remove(type);
}
@Override
public boolean deleteResource(Resource resource) {
return resourceList.remove(resource);
public void clearAllResources() {
resourceMap.clear();
}
@Override
public void clearResources() {
resourceList.clear();
public Map<String, List<Resource>> listAllResources() {
return resourceMap;
}
@Override
public void addTemplate(Map<String, Resource> template) {
templateResourceList.add(template);
public List<Resource> listResourcesByType(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList());
}
@Override
public void setTemplates(List<Map<String, Resource>> templateResource) {
templateResourceList = new ArrayList<>(templateResource);
}
@Override
public void deleteTemplate(Map<String, Resource> template) {
templateResourceList.remove(template);
}
@Override
public void clearTemplates() {
templateResourceList.clear();
}
@Override
public List<Resource> listResources() {
return Collections.unmodifiableList(resourceList);
}
@Override
public List<Map<String, Resource>> listTemplates() {
return Collections.unmodifiableList(templateResourceList);
}
@Override
public int getResourceCount() {
return resourceList.size();
}
@Override
public int getTemplateCount() {
return templateResourceList.size();
}
@Override
public Resource getResourceByIndex(int index) {
if (index < 0 || index > resourceList.size() - 1) {
throw new IllegalArgumentException("错误的index");
public Resource randomGetResource(String type) {
List<Resource> resources = resourceMap.get(type);
if (CollectionUtils.isEmpty(resources)) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
}
return resourceList.get(index);
if (resources.size() == 1) {
return resources.get(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(resources.size());
return resources.get(randomIndex);
}
@Override
public Map<String, Resource> getTemplateByIndex(int index) {
if (index < 0 || index > templateResourceList.size() - 1) {
throw new IllegalArgumentException("错误的index");
public int getAllResourceCount() {
int count = 0;
for (List<Resource> value : resourceMap.values()) {
count += value.size();
}
return templateResourceList.get(index);
return count;
}
@Override
public int getResourceCount(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList()).size();
}
@Override
public void addTemplate(String type, Map<String, Resource> template) {
templateResourceMap.computeIfAbsent(type, k -> new ArrayList<>(2)).add(template);
}
@Override
public void clearAllTemplates() {
templateResourceMap.clear();
}
@Override
public void clearTemplates(String type) {
templateResourceMap.remove(type);
}
@Override
public List<Map<String, Resource>> listTemplatesByType(String type) {
return templateResourceMap.getOrDefault(type, Collections.emptyList());
}
@Override
public Map<String, List<Map<String, Resource>>> listAllTemplates() {
return templateResourceMap;
}
@Override
public Map<String, Resource> randomGetTemplateByType(String type) {
List<Map<String, Resource>> templateList = templateResourceMap.get(type);
if (CollectionUtils.isEmpty(templateList)) {
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
}
if (templateList.size() == 1) {
return templateList.get(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(templateList.size());
return templateList.get(randomIndex);
}
}
@@ -13,7 +13,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: 天爱有情
@@ -47,29 +46,21 @@ public class DefaultSliderCaptchaResourceManager implements SliderCaptchaResourc
}
@Override
public Map<String, Resource> randomGetTemplate() {
int count = resourceStore.getTemplateCount();
if (count < 1) {
throw new IllegalStateException("随机获取模板错误,store中模板为空");
public Map<String, Resource> randomGetTemplate(String type) {
Map<String, Resource> resourceMap = resourceStore.randomGetTemplateByType(type);
if (resourceMap == null) {
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
}
if (count == 1) {
return resourceStore.getTemplateByIndex(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(count);
return resourceStore.getTemplateByIndex(randomIndex);
return resourceMap;
}
@Override
public Resource randomGetResource() {
int count = resourceStore.getResourceCount();
if (count < 1) {
throw new IllegalStateException("随机获取资源错误,store中资源为空");
public Resource randomGetResource(String type) {
Resource resource = resourceStore.randomGetResource(type);
if (resource == null) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
}
if (count == 1) {
return resourceStore.getResourceByIndex(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(count);
return resourceStore.getResourceByIndex(randomIndex);
return resource;
}
@Override
@@ -1,6 +1,6 @@
package cloud.tianai.captcha.template.slider.validator;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
import java.util.Map;
@@ -43,10 +43,10 @@ public interface SliderCaptchaValidator {
/**
* 用于生成验证码校验时需要的回传参数
*
* @param sliderCaptchaInfo 生成的验证码数据
* @param imageCaptchaInfo 生成的验证码数据
* @return Map<String, Object>
*/
Map<String, Object> generateSliderCaptchaValidData(SliderCaptchaInfo sliderCaptchaInfo);
Map<String, Object> generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo);
/**
* 校验用户滑动滑块是否正确
@@ -1,7 +1,7 @@
package cloud.tianai.captcha.template.slider.validator.impl;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo;
import cloud.tianai.captcha.template.slider.common.util.CollectionUtils;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator;
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
import lombok.Getter;
@@ -56,9 +56,9 @@ public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator {
}
@Override
public Map<String, Object> generateSliderCaptchaValidData(SliderCaptchaInfo sliderCaptchaInfo) {
public Map<String, Object> generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
Map<String, Object> map = new HashMap<>(8);
addPercentage(sliderCaptchaInfo, map);
addPercentage(imageCaptchaInfo, map);
return map;
}
@@ -106,8 +106,8 @@ public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator {
return null;
}
protected void addPercentage(SliderCaptchaInfo sliderCaptchaInfo, Map<String, Object> sliderCaptchaValidData) {
float percentage = calcPercentage(sliderCaptchaInfo.getX(), sliderCaptchaInfo.getBgImageWidth());
protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> sliderCaptchaValidData) {
float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth());
sliderCaptchaValidData.put("percentage", percentage);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,37 @@
package example;
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.template.slider.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.RotateImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.impl.StandardRotateCaptchaGenerator;
import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import cloud.tianai.captcha.template.slider.resource.impl.DefaultResourceStore;
import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaResourceManager;
import java.util.HashMap;
import java.util.Map;
public class StandardRotateCaptchaGeneratorTest {
public static void main(String[] args) {
ResourceStore resourceStore = new DefaultResourceStore();
// Map<String, Resource> template = new HashMap<>();
// template.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\a.png"));
// template.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource("file", "C:\\Users\\Thinkpad\\Desktop\\b.png"));
// resourceStore.addTemplate(CaptchaTypeConstant.ROTATE, template);
// resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource("file", "E:\\projects\\tianai-captcha\\src\\main\\resources\\META-INF\\cut-image\\resource\\1.jpg"));
SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(resourceStore);
StandardRotateCaptchaGenerator standardRotateCaptchaGenerator = new StandardRotateCaptchaGenerator(sliderCaptchaResourceManager, true);
ImageCaptchaInfo imageCaptchaInfo = standardRotateCaptchaGenerator.generateCaptchaImage(CaptchaTypeConstant.ROTATE);
System.out.println("backgroundImage:" + imageCaptchaInfo.getBackgroundImage());
System.out.println("sliderImage:" + imageCaptchaInfo.getSliderImage());
System.out.println(((RotateImageCaptchaInfo)imageCaptchaInfo).getDegree());
}
}