旋转图片

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
+5 -3
View File
@@ -34,6 +34,7 @@
### 2. 使用 `SliderCaptchaGenerator`生成器生成滑块验证码 ### 2. 使用 `SliderCaptchaGenerator`生成器生成滑块验证码
```java ```java
import cloud.tianai.captcha.template.slider.generator.impl.StandardImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.impl.StandardSliderCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.impl.StandardSliderCaptchaGenerator;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaResourceManager;
@@ -41,9 +42,9 @@ import cloud.tianai.captcha.template.slider.resource.impl.DefaultSliderCaptchaRe
public class Test { public class Test {
public static void main(String[] args) throws InterruptedException { public static void main(String[] args) throws InterruptedException {
SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager(); SliderCaptchaResourceManager sliderCaptchaResourceManager = new DefaultSliderCaptchaResourceManager();
StandardSliderCaptchaGenerator sliderCaptchaGenerator = new StandardSliderCaptchaGenerator(sliderCaptchaResourceManager, true); StandardImageCaptchaGenerator sliderCaptchaGenerator = new StandardImageCaptchaGenerator(sliderCaptchaResourceManager, true);
// 生成滑块图片 // 生成滑块图片
SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateSlideImageInfo(); SliderCaptchaInfo slideImageInfo = sliderCaptchaGenerator.generateCaptchaImage();
System.out.println(slideImageInfo); System.out.println(slideImageInfo);
// 负责计算一些数据存到缓存中,用于校验使用 // 负责计算一些数据存到缓存中,用于校验使用
@@ -58,11 +59,12 @@ public class Test {
### 3. 使用`SliderCaptchaValidator`校验器 验证 ### 3. 使用`SliderCaptchaValidator`校验器 验证
```java ```java
import cloud.tianai.captcha.template.slider.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator;
public class Test2 { public class Test2 {
public static void main(String[] args) { public static void main(String[] args) {
SliderCaptchaGenerator sliderCaptchaValidator = new BasicCaptchaTrackValidator(); ImageCaptchaGenerator sliderCaptchaValidator = new BasicCaptchaTrackValidator();
// 用户传来的行为轨迹和进行校验 // 用户传来的行为轨迹和进行校验
// - sliderCaptchaTrack为前端传来的滑动轨迹数据 // - sliderCaptchaTrack为前端传来的滑动轨迹数据
@@ -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; 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.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.resource.SliderCaptchaResourceManager;
import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator; import cloud.tianai.captcha.template.slider.validator.SliderCaptchaValidator;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
* @date 2020/10/19 18:37 * @date 2020/10/19 18:37
* @Description 滑块验证码模板 * @Description 图片验证码生成器
*/ */
public interface SliderCaptchaGenerator { public interface ImageCaptchaGenerator {
/** /**
* 获取滑块验证码 * 生成验证码图片
* * @param type 类型 {@link CaptchaTypeConstant}
* @return SliderCaptchaInfo * @return SliderCaptchaInfo
*/ */
SliderCaptchaInfo generateSlideImageInfo(); ImageCaptchaInfo generateCaptchaImage(String type);
/** /**
* 生成滑块验证码 * 生成滑块验证码
* * @param type type {@link CaptchaTypeConstant}
* @param targetFormatName jpeg或者webp格式 * @param targetFormatName jpeg或者webp格式
* @param matrixFormatName png或者webp格式 * @param matrixFormatName png或者webp格式
* @return SliderCaptchaInfo * @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 生成参数 * @param param 生成参数
* @return SliderCaptchaInfo * @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 String sliderFormatName = "png";
/** 是否混淆.*/ /** 是否混淆.*/
private Boolean obfuscate = false; 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; 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.common.model.dto.GenerateParam;
import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator; import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderCaptchaInfo;
import cloud.tianai.captcha.template.slider.common.util.NamedThreadFactory; import cloud.tianai.captcha.template.slider.common.util.NamedThreadFactory;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import lombok.Getter; import lombok.Getter;
@@ -22,12 +22,12 @@ import java.util.concurrent.atomic.AtomicInteger;
* @Description 滑块验证码缓冲器 * @Description 滑块验证码缓冲器
*/ */
@Slf4j @Slf4j
public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator { public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
protected final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("slider-captcha-queue")); 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 AtomicInteger pos = new AtomicInteger(0);
protected SliderCaptchaGenerator target; protected ImageCaptchaGenerator target;
protected int size; protected int size;
/** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */ /** 等待时间,一般报错或者拉取为空时会休眠一段时间再试. */
protected int waitTime = 1000; protected int waitTime = 1000;
@@ -39,13 +39,13 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
@Setter @Setter
protected boolean requiredGetCaptcha = true; protected boolean requiredGetCaptcha = true;
public CacheSliderCaptchaGenerator(SliderCaptchaGenerator target, GenerateParam generateParam, int size) { public CacheImageCaptchaGenerator(ImageCaptchaGenerator target, GenerateParam generateParam, int size) {
this.target = target; this.target = target;
this.generateParam = generateParam; this.generateParam = generateParam;
this.size = size; 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.target = target;
this.generateParam = generateParam; this.generateParam = generateParam;
this.size = size; this.size = size;
@@ -71,7 +71,7 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
if (pos.get() >= size) { if (pos.get() >= size) {
return; return;
} }
SliderCaptchaInfo slideImageInfo = target.generateSlideImageInfo(generateParam); ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(generateParam);
if (slideImageInfo != null) { if (slideImageInfo != null) {
boolean addStatus = queue.offer(slideImageInfo); boolean addStatus = queue.offer(slideImageInfo);
if (addStatus) { if (addStatus) {
@@ -101,17 +101,17 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
@SneakyThrows @SneakyThrows
@Override @Override
public SliderCaptchaInfo generateSlideImageInfo() { public ImageCaptchaInfo generateCaptchaImage(String type) {
return generateSlideImageInfo(this.requiredGetCaptcha); return generateCaptchaImage(this.requiredGetCaptcha);
} }
@SneakyThrows @SneakyThrows
public SliderCaptchaInfo generateSlideImageInfo(boolean requiredGetCaptcha) { public ImageCaptchaInfo generateCaptchaImage(boolean requiredGetCaptcha) {
SliderCaptchaInfo poll = queue.poll(); ImageCaptchaInfo poll = queue.poll();
if (poll == null && requiredGetCaptcha) { if (poll == null && requiredGetCaptcha) {
log.warn("滑块验证码缓存不足, genParam:{}", generateParam); log.warn("滑块验证码缓存不足, genParam:{}", generateParam);
// 如果池内没数据 则直接生成 // 如果池内没数据 则直接生成
return target.generateSlideImageInfo(generateParam); return target.generateCaptchaImage(generateParam);
} }
// 减1 // 减1
pos.decrementAndGet(); pos.decrementAndGet();
@@ -119,18 +119,13 @@ public class CacheSliderCaptchaGenerator implements SliderCaptchaGenerator {
} }
@Override @Override
public SliderCaptchaInfo generateSlideImageInfo(String targetFormatName, String matrixFormatName) { public ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName) {
return target.generateSlideImageInfo(targetFormatName, matrixFormatName); return target.generateCaptchaImage(type,targetFormatName, matrixFormatName);
} }
@Override @Override
public SliderCaptchaInfo generateSlideImageInfo(GenerateParam param) { public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
return target.generateSlideImageInfo(param); return target.generateCaptchaImage(param);
}
@Override
public boolean percentageContrast(Float newPercentage, Float oriPercentage) {
return target.percentageContrast(newPercentage, oriPercentage);
} }
@Override @Override
@@ -1,24 +1,25 @@
package cloud.tianai.captcha.template.slider.generator.impl; 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.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.template.slider.generator.SliderCaptchaGenerator; 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.impl.provider.ClassPathResourceProvider; import cloud.tianai.captcha.template.slider.generator.common.model.dto.SliderImageCaptchaInfo;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import cloud.tianai.captcha.template.slider.resource.ResourceStore; import cloud.tianai.captcha.template.slider.resource.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager; import cloud.tianai.captcha.template.slider.resource.SliderCaptchaResourceManager;
import lombok.Getter; import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import lombok.Setter; import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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 java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.*; 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 滑块验证码模板 * @Description 滑块验证码模板
*/ */
@Slf4j @Slf4j
public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator { public class StandardImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/** /**
* 默认的resource资源文件路径. * 默认的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 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; 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, public StandardImageCaptchaGenerator(SliderCaptchaResourceManager sliderCaptchaResourceManager,
boolean initDefaultResource) { boolean initDefaultResource) {
this.sliderCaptchaResourceManager = sliderCaptchaResourceManager; this.sliderCaptchaResourceManager = sliderCaptchaResourceManager;
if (initDefaultResource) { if (initDefaultResource) {
initDefaultResource(); initDefaultResource();
} }
} }
@Override
public SliderCaptchaInfo generateSlideImageInfo() {
return generateSlideImageInfo(defaultBgImageType, defaultSliderImageType);
}
@SneakyThrows @SneakyThrows
@Override @Override
public SliderCaptchaInfo generateSlideImageInfo(String backgroundFormatName, String sliderFormatName) { public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
return generateSlideImageInfo(GenerateParam.builder()
.backgroundFormatName(backgroundFormatName)
.sliderFormatName(sliderFormatName)
.obfuscate(false)
.build());
}
@SneakyThrows
@Override
public SliderCaptchaInfo generateSlideImageInfo(GenerateParam param) {
Boolean obfuscate = param.getObfuscate(); Boolean obfuscate = param.getObfuscate();
Map<String, Resource> templateImages = sliderCaptchaResourceManager.randomGetTemplate(); Map<String, Resource> templateImages = sliderCaptchaResourceManager.randomGetTemplate(param.getType());
if (templateImages == null || templateImages.isEmpty()) { if (templateImages == null || templateImages.isEmpty()) {
return null; return null;
} }
Collection<InputStream> inputStreams = new LinkedList<>(); Collection<InputStream> inputStreams = new LinkedList<>();
try { try {
Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(); Resource resourceImage = sliderCaptchaResourceManager.randomGetResource(param.getType());
InputStream resourceInputStream = sliderCaptchaResourceManager.getResourceInputStream(resourceImage); InputStream resourceInputStream = sliderCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream); inputStreams.add(resourceInputStream);
BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream); BufferedImage cutBackground = wrapFile2BufferedImage(resourceInputStream);
@@ -151,16 +121,16 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
* @return SliderCaptchaInfo * @return SliderCaptchaInfo
*/ */
@SneakyThrows @SneakyThrows
public SliderCaptchaInfo wrapSliderCaptchaInfo(int randomX, public SliderImageCaptchaInfo wrapSliderCaptchaInfo(int randomX,
int randomY, int randomY,
BufferedImage backgroundImage, BufferedImage backgroundImage,
BufferedImage sliderImage, BufferedImage sliderImage,
GenerateParam param) { GenerateParam param) {
String backgroundFormatName = param.getBackgroundFormatName(); String backgroundFormatName = param.getBackgroundFormatName();
String sliderFormatName = param.getSliderFormatName(); String sliderFormatName = param.getSliderFormatName();
String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName); String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName);
String sliderImageBase64 = transform(sliderImage, sliderFormatName); String sliderImageBase64 = transform(sliderImage, sliderFormatName);
return SliderCaptchaInfo.of(randomX, randomY, return SliderImageCaptchaInfo.of(randomX, randomY,
backGroundImageBase64, backGroundImageBase64,
sliderImageBase64, sliderImageBase64,
backgroundImage.getWidth(), backgroundImage.getHeight(), 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 @Override
public SliderCaptchaResourceManager getSlideImageResourceManager() { public SliderCaptchaResourceManager getSlideImageResourceManager() {
return sliderCaptchaResourceManager; return sliderCaptchaResourceManager;
@@ -217,34 +152,26 @@ public class StandardSliderCaptchaGenerator implements SliderCaptchaGenerator {
return ThreadLocalRandom.current().nextInt(slWidth, sliderX - slWidth); 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() { public void initDefaultResource() {
ResourceStore resourceStore = sliderCaptchaResourceManager.getResourceStore(); 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); 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_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_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"))); 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); 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_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_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"))); 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 资源 * @param resource 资源
*/ */
void addResource(Resource resource); void addResource(String type, Resource resource);
/** /**
* 设置资源 * 清除某个类型下的所有资源
* *
* @param resources resources * @param type type
*/ */
void setResource(List<Resource> resources); void clearResources(String type);
/**
* 删除资源
*
* @param resource resource
*/
boolean deleteResource(Resource resource);
/** /**
* 清除所有资源 * 清除所有资源
*/ */
void clearResources(); void clearAllResources();
/**
* 添加模板
*
* @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();
/** /**
* 获取所有资源对象 * 获取所有资源对象
* *
* @return List<Resource> * @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 * @return int
*/ */
int getResourceCount(); int getAllResourceCount();
/** /**
* 获取模板count * 获取某个type下的资源总数
* *
* @param type type
* @return int * @return int
*/ */
int getTemplateCount(); int getResourceCount(String type);
/** /**
* 获取资源通过index * 添加模板
* *
* @param index index * @param template template
* @return Resource
*/ */
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> * @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> * @return Map<String, Resource>
*/ */
Map<String, Resource> randomGetTemplate(); Map<String, Resource> randomGetTemplate(String type);
/** /**
* 随机获取某个资源对象 * 随机获取某个资源对象
* *
* @return Resource * @return Resource
*/ */
Resource randomGetResource(); Resource randomGetResource(String type);
/** /**
* 获取真正的资源流通过资源对象 * 获取真正的资源流通过资源对象
@@ -1,12 +1,11 @@
package cloud.tianai.captcha.template.slider.resource.impl; 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.ResourceStore;
import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource;
import java.util.ArrayList; import java.util.*;
import java.util.Collections; import java.util.concurrent.ThreadLocalRandom;
import java.util.List;
import java.util.Map;
/** /**
* @Author: 天爱有情 * @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. * resource.
*/ */
private List<Resource> resourceList = new ArrayList<>(20); private Map<String, List<Resource>> resourceMap = new HashMap<>(2);
@Override @Override
public void addResource(Resource resource) { public void addResource(String type, Resource resource) {
resourceList.add(resource); resourceMap.computeIfAbsent(type, k -> new ArrayList<>(20)).add(resource);
} }
@Override @Override
public void setResource(List<Resource> resources) { public void clearResources(String type) {
resourceList = new ArrayList<>(resources); resourceMap.remove(type);
} }
@Override @Override
public boolean deleteResource(Resource resource) { public void clearAllResources() {
return resourceList.remove(resource); resourceMap.clear();
} }
@Override @Override
public void clearResources() { public Map<String, List<Resource>> listAllResources() {
resourceList.clear(); return resourceMap;
} }
@Override @Override
public void addTemplate(Map<String, Resource> template) { public List<Resource> listResourcesByType(String type) {
templateResourceList.add(template); return resourceMap.getOrDefault(type, Collections.emptyList());
} }
@Override @Override
public void setTemplates(List<Map<String, Resource>> templateResource) { public Resource randomGetResource(String type) {
templateResourceList = new ArrayList<>(templateResource); List<Resource> resources = resourceMap.get(type);
} if (CollectionUtils.isEmpty(resources)) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
@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");
} }
return resourceList.get(index); if (resources.size() == 1) {
return resources.get(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(resources.size());
return resources.get(randomIndex);
} }
@Override @Override
public Map<String, Resource> getTemplateByIndex(int index) { public int getAllResourceCount() {
if (index < 0 || index > templateResourceList.size() - 1) { int count = 0;
throw new IllegalArgumentException("错误的index"); 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.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
/** /**
* @Author: 天爱有情 * @Author: 天爱有情
@@ -47,29 +46,21 @@ public class DefaultSliderCaptchaResourceManager implements SliderCaptchaResourc
} }
@Override @Override
public Map<String, Resource> randomGetTemplate() { public Map<String, Resource> randomGetTemplate(String type) {
int count = resourceStore.getTemplateCount(); Map<String, Resource> resourceMap = resourceStore.randomGetTemplateByType(type);
if (count < 1) { if (resourceMap == null) {
throw new IllegalStateException("随机获取模板错误,store中模板为空"); throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
} }
if (count == 1) { return resourceMap;
return resourceStore.getTemplateByIndex(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(count);
return resourceStore.getTemplateByIndex(randomIndex);
} }
@Override @Override
public Resource randomGetResource() { public Resource randomGetResource(String type) {
int count = resourceStore.getResourceCount(); Resource resource = resourceStore.randomGetResource(type);
if (count < 1) { if (resource == null) {
throw new IllegalStateException("随机获取资源错误,store中资源为空"); throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
} }
if (count == 1) { return resource;
return resourceStore.getResourceByIndex(0);
}
int randomIndex = ThreadLocalRandom.current().nextInt(count);
return resourceStore.getResourceByIndex(randomIndex);
} }
@Override @Override
@@ -1,6 +1,6 @@
package cloud.tianai.captcha.template.slider.validator; 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 cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
import java.util.Map; import java.util.Map;
@@ -43,10 +43,10 @@ public interface SliderCaptchaValidator {
/** /**
* 用于生成验证码校验时需要的回传参数 * 用于生成验证码校验时需要的回传参数
* *
* @param sliderCaptchaInfo 生成的验证码数据 * @param imageCaptchaInfo 生成的验证码数据
* @return Map<String, Object> * @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; 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.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.SliderCaptchaValidator;
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack; import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
import lombok.Getter; import lombok.Getter;
@@ -56,9 +56,9 @@ public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator {
} }
@Override @Override
public Map<String, Object> generateSliderCaptchaValidData(SliderCaptchaInfo sliderCaptchaInfo) { public Map<String, Object> generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
Map<String, Object> map = new HashMap<>(8); Map<String, Object> map = new HashMap<>(8);
addPercentage(sliderCaptchaInfo, map); addPercentage(imageCaptchaInfo, map);
return map; return map;
} }
@@ -106,8 +106,8 @@ public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator {
return null; return null;
} }
protected void addPercentage(SliderCaptchaInfo sliderCaptchaInfo, Map<String, Object> sliderCaptchaValidData) { protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> sliderCaptchaValidData) {
float percentage = calcPercentage(sliderCaptchaInfo.getX(), sliderCaptchaInfo.getBgImageWidth()); float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth());
sliderCaptchaValidData.put("percentage", percentage); 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());
}
}