重构 resource 模块,支持自定义tag标签

重构 transform 模块
修复以及优化各验证码生成器,
This commit is contained in:
天爱有情
2023-02-13 14:29:11 +08:00
parent ebc39efb02
commit 37d88b5ec2
25 changed files with 518 additions and 346 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>cloud.tianai.captcha</groupId>
<artifactId>tianai-captcha</artifactId>
<version>1.3.3</version>
<version>1.3.3.1</version>
<name>tianai-captcha</name>
<description>行为验证码</description>
@@ -0,0 +1,8 @@
package cloud.tianai.captcha.common.constant;
public interface CommonConstant {
String DEFAULT_TAG = "default";
/** 图标点选资源存储类型.*/
String IMAGE_CLICK_ICON = "ICON";
}
@@ -4,16 +4,23 @@ import cloud.tianai.captcha.common.exception.ImageCaptchaException;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.generator.impl.transform.Base64ImageTransform;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: 天爱有情
@@ -78,11 +85,11 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
@SneakyThrows
@Override
public ImageCaptchaInfo generateCaptchaImage(String type, String backgroundFormatName, String sliderFormatName) {
public ImageCaptchaInfo generateCaptchaImage(String type, String backgroundFormatName, String templateFormatName) {
return generateCaptchaImage(GenerateParam.builder()
.type(type)
.backgroundFormatName(backgroundFormatName)
.sliderFormatName(sliderFormatName)
.templateFormatName(templateFormatName)
.obfuscate(false)
.build());
}
@@ -94,18 +101,18 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
}
protected Map<String, Resource> requiredRandomGetTemplate(String type) {
Map<String, Resource> templateMap = imageCaptchaResourceManager.randomGetTemplate(type);
protected ResourceMap requiredRandomGetTemplate(String type, String tag) {
ResourceMap templateMap = imageCaptchaResourceManager.randomGetTemplate(type, tag);
if (CollectionUtils.isEmpty(templateMap)) {
throw new ImageCaptchaException("随机获取模板资源失败, 获取到的资源为空, type=" + type);
throw new ImageCaptchaException("随机获取模板资源失败, 获取到的资源为空, type=" + type + ",tag=" + tag);
}
return templateMap;
}
protected Resource requiredRandomGetResource(String type) {
Resource resource = imageCaptchaResourceManager.randomGetResource(type);
protected Resource requiredRandomGetResource(String type, String tag) {
Resource resource = imageCaptchaResourceManager.randomGetResource(type, tag);
if (resource == null) {
throw new ImageCaptchaException("随机获取资源失败, 获取到的资源为空, type=" + type);
throw new ImageCaptchaException("随机获取资源失败, 获取到的资源为空, type=" + type + ",tag=" + tag);
}
return resource;
}
@@ -116,9 +123,71 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
if (resource == null) {
throw new IllegalArgumentException("查找模板异常, 该模板下未找到 ".concat(imageName));
}
return getImageResourceManager().getResourceInputStream(resource);
return getResourceInputStream(resource, null);
}
protected BufferedImage getTemplateImage(Map<String, Resource> templateImages, String imageName) {
InputStream stream = getTemplateFile(templateImages, imageName);
BufferedImage bufferedImage = CaptchaImageUtils.wrapFile2BufferedImage(stream);
closeStream(stream);
return bufferedImage;
}
protected BufferedImage getResourceImage(Resource resource) {
InputStream stream = getResourceInputStream(resource, null);
BufferedImage bufferedImage = CaptchaImageUtils.wrapFile2BufferedImage(stream);
closeStream(stream);
return bufferedImage;
}
protected int randomInt(int origin, int bound) {
return ThreadLocalRandom.current().nextInt(origin, bound);
}
protected boolean randomBoolean() {
return ThreadLocalRandom.current().nextBoolean();
}
protected int randomInt(int bound) {
return ThreadLocalRandom.current().nextInt(bound);
}
public void closeStream(InputStream stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// ignore
}
}
}
protected InputStream getResourceInputStream(Resource resource, Collection<InputStream> inputStreams) {
InputStream stream = getImageResourceManager().getResourceInputStream(resource);
if (stream != null && inputStreams != null) {
inputStreams.add(stream);
}
return stream;
}
protected Optional<BufferedImage> getTemplateImageOfOptional(Map<String, Resource> templateImages, String imageName) {
Optional<InputStream> optional = getTemplateFileOfOptional(templateImages, imageName);
if (optional.isPresent()) {
InputStream inputStream = optional.get();
BufferedImage bufferedImage = CaptchaImageUtils.wrapFile2BufferedImage(inputStream);
closeStream(inputStream);
return Optional.ofNullable(bufferedImage);
}
return Optional.empty();
}
protected Optional<InputStream> getTemplateFileOfOptional(Map<String, Resource> templateImages, String imageName) {
Resource resource = templateImages.get(imageName);
if (resource == null) {
return Optional.empty();
}
return Optional.ofNullable(getResourceInputStream(resource, null));
}
protected void assertInit() {
if (!init) {
@@ -1,5 +1,9 @@
package cloud.tianai.captcha.generator;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import java.awt.image.BufferedImage;
/**
@@ -12,9 +16,28 @@ public interface ImageTransform {
/**
* 转换
*
* @param bufferedImage 图片
* @param transformType 转换类型
* @param backgroundImage 背景图片
* @param param 参数
* @param backgroundResource 背景资源对象
* @return ImageTransformData
*/
default ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, Resource backgroundResource) {
return transform(param, backgroundImage, null, backgroundResource, null);
}
/**
* 转换
*
* @param backgroundImage 背景图片
* @param templateImage 模板图片(可能为空)
* @param param 参数
* @param backgroundResource 背景资源对象
* @param templateResource 模板资源对象(可能为空)
* @return String
*/
String transform(BufferedImage bufferedImage, String transformType);
ImageTransformData transform(GenerateParam param,
BufferedImage backgroundImage,
BufferedImage templateImage,
Object backgroundResource,
Object templateResource);
}
@@ -1,5 +1,6 @@
package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -14,7 +15,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class ClickImageCheckDefinition {
/** 提示.*/
private String tip;
private Resource tip;
/** x.*/
private Integer x;
/** y.*/
@@ -24,4 +25,4 @@ public class ClickImageCheckDefinition {
/** 高.*/
private Integer height;
}
}
@@ -1,7 +1,7 @@
package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CommonConstant;
import lombok.*;
/**
@@ -15,12 +15,18 @@ import lombok.*;
@AllArgsConstructor
@EqualsAndHashCode
public class GenerateParam {
/** 背景格式化名称.*/
/** 背景格式化类型. */
private String backgroundFormatName = "jpeg";
/** 滑块格式化名称.*/
private String sliderFormatName = "png";
/** 是否混淆.*/
/** 模板图片格式化类型. */
private String templateFormatName = "png";
/** 是否混淆. */
private Boolean obfuscate = false;
/** 类型.*/
/** 类型. */
private String type = CaptchaTypeConstant.SLIDER;
/** 背景图片标签, 用户二级过滤背景图片,或指定某背景图片. */
private String backgroundImageTag = CommonConstant.DEFAULT_TAG;
/** 滑动图片标签,用户二级过滤模板图片,或指定某模板图片.. */
private String templateImageTag = CommonConstant.DEFAULT_TAG;
/** 扩展参数.*/
private Object param;
}
@@ -14,29 +14,29 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class ImageCaptchaInfo {
/**
* 背景图
*/
/** 背景图. */
private String backgroundImage;
/**
* 移动图
*/
private String sliderImage;
/** 模板图. */
private String templateImage;
/** 背景图片所属标签. */
private String backgroundImageTag;
/** 模板图片所属标签. */
private String templateImageTag;
/** 背景图片宽度. */
private Integer bgImageWidth;
private Integer backgroundImageWidth;
/** 背景图片高度. */
private Integer bgImageHeight;
private Integer backgroundImageHeight;
/** 滑块图片宽度. */
private Integer sliderImageWidth;
private Integer templateImageWidth;
/** 滑块图片高度. */
private Integer sliderImageHeight;
private Integer templateImageHeight;
/** 随机值. */
private Integer randomX;
/** 容错值, 可以为空 默认 0.02容错,校验的时候用. */
private Float tolerant;
/** 验证码类型. */
private String type;
/** 透传字段,用于传给前端.*/
/** 透传字段,用于传给前端. */
private Object data;
/**
* 扩展字段
@@ -44,32 +44,46 @@ public class ImageCaptchaInfo {
public Object expand;
public ImageCaptchaInfo(String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight,
String templateImage,
String backgroundImageTag,
String templateImageTag,
Integer backgroundImageWidth,
Integer backgroundImageHeight,
Integer templateImageWidth,
Integer templateImageHeight,
Integer randomX,
String type) {
this.backgroundImage = backgroundImage;
this.sliderImage = sliderImage;
this.bgImageWidth = bgImageWidth;
this.bgImageHeight = bgImageHeight;
this.sliderImageWidth = sliderImageWidth;
this.sliderImageHeight = sliderImageHeight;
this.templateImage = templateImage;
this.backgroundImageTag = backgroundImageTag;
this.templateImageTag = templateImageTag;
this.backgroundImageWidth = backgroundImageWidth;
this.backgroundImageHeight = backgroundImageHeight;
this.templateImageWidth = templateImageWidth;
this.templateImageHeight = templateImageHeight;
this.randomX = randomX;
this.type = type;
}
public static ImageCaptchaInfo of(String backgroundImage,
String sliderImage,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight,
Integer randomKey,
String templateImage,
String backgroundImageTag,
String templateImageTag,
Integer backgroundImageWidth,
Integer backgroundImageHeight,
Integer templateImageWidth,
Integer templateImageHeight,
Integer randomX,
String type) {
return new ImageCaptchaInfo(backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey, type);
return new ImageCaptchaInfo(backgroundImage,
templateImage,
backgroundImageTag,
templateImageTag,
backgroundImageWidth,
backgroundImageHeight,
templateImageWidth,
templateImageHeight,
randomX, type);
}
}
@@ -0,0 +1,25 @@
package cloud.tianai.captcha.generator.common.model.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: 天爱有情
* @date 2023/1/5 11:39
* @Description 图片转换成url后的对象
*/
@Data
@NoArgsConstructor
public class ImageTransformData {
/** 背景图. */
private String backgroundImageUrl;
/** 模板图. */
private String templateImageUrl;
/** 留一个扩展数据. */
private Object data;
public ImageTransformData(String backgroundImageUrl, String templateImageUrl) {
this.backgroundImageUrl = backgroundImageUrl;
this.templateImageUrl = templateImageUrl;
}
}
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -25,21 +24,25 @@ public class RotateImageCaptchaInfo extends ImageCaptchaInfo {
public static RotateImageCaptchaInfo of(Double degree,
Integer randomX,
String backgroundImage,
String sliderImage,
String templateImage,
String backgroundImageTag,
String templateImageTag,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
Integer sliderImageHeight) {
Integer templateImageWidth,
Integer templateImageHeight) {
RotateImageCaptchaInfo rotateImageCaptchaInfo = new RotateImageCaptchaInfo();
rotateImageCaptchaInfo.setDegree(degree);
rotateImageCaptchaInfo.setRandomX(randomX);
rotateImageCaptchaInfo.setBackgroundImage(backgroundImage);
rotateImageCaptchaInfo.setBackgroundImageTag(backgroundImageTag);
rotateImageCaptchaInfo.setTemplateImage(templateImageTag);
rotateImageCaptchaInfo.setTolerant(DEFAULT_TOLERANT);
rotateImageCaptchaInfo.setSliderImage(sliderImage);
rotateImageCaptchaInfo.setBgImageWidth(bgImageWidth);
rotateImageCaptchaInfo.setBgImageHeight(bgImageHeight);
rotateImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
rotateImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
rotateImageCaptchaInfo.setTemplateImage(templateImage);
rotateImageCaptchaInfo.setBackgroundImageWidth(bgImageWidth);
rotateImageCaptchaInfo.setBackgroundImageHeight(bgImageHeight);
rotateImageCaptchaInfo.setTemplateImageWidth(templateImageWidth);
rotateImageCaptchaInfo.setTemplateImageHeight(templateImageHeight);
// 类型为旋转图片验证码
rotateImageCaptchaInfo.setType(CaptchaTypeConstant.ROTATE);
return rotateImageCaptchaInfo;
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.generator.common.model.dto;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -23,7 +22,9 @@ public class SliderImageCaptchaInfo extends ImageCaptchaInfo {
public static SliderImageCaptchaInfo of(Integer x,
Integer y,
String backgroundImage,
String sliderImage,
String templateImage,
String backgroundImageTag,
String templateImageTag,
Integer bgImageWidth,
Integer bgImageHeight,
Integer sliderImageWidth,
@@ -33,11 +34,13 @@ public class SliderImageCaptchaInfo extends ImageCaptchaInfo {
sliderImageCaptchaInfo.setY(y);
sliderImageCaptchaInfo.setRandomX(x);
sliderImageCaptchaInfo.setBackgroundImage(backgroundImage);
sliderImageCaptchaInfo.setSliderImage(sliderImage);
sliderImageCaptchaInfo.setBgImageWidth(bgImageWidth);
sliderImageCaptchaInfo.setBgImageHeight(bgImageHeight);
sliderImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
sliderImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
sliderImageCaptchaInfo.setTemplateImage(templateImage);
sliderImageCaptchaInfo.setBackgroundImageTag(backgroundImageTag);
sliderImageCaptchaInfo.setTemplateImageTag(templateImageTag);
sliderImageCaptchaInfo.setBackgroundImageWidth(bgImageWidth);
sliderImageCaptchaInfo.setBackgroundImageHeight(bgImageHeight);
sliderImageCaptchaInfo.setTemplateImageWidth(sliderImageWidth);
sliderImageCaptchaInfo.setTemplateImageHeight(sliderImageHeight);
sliderImageCaptchaInfo.setType(CaptchaTypeConstant.SLIDER);
return sliderImageCaptchaInfo;
}
@@ -7,12 +7,14 @@ import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -22,14 +24,6 @@ import java.util.concurrent.ThreadLocalRandom;
*/
public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
/** 参与校验的数量. */
@Getter
@Setter
protected Integer checkClickCount = 4;
/** 干扰数量. */
@Getter
@Setter
protected Integer interferenceCount = 2;
public AbstractClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager) {
super(imageCaptchaResourceManager);
@@ -42,89 +36,62 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
@Override
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
// 文字点选验证码不需要模板 只需要背景图
Collection<InputStream> inputStreams = new LinkedList<>();
try {
Resource resourceImage = requiredRandomGetResource(param.getType());
InputStream resourceInputStream = getImageResourceManager().getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream);
BufferedImage bgImage = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
List<ClickImageCheckDefinition> clickImageCheckDefinitionList = new ArrayList<>(interferenceCount);
int allImages = interferenceCount + checkClickCount;
int avg = bgImage.getWidth() / allImages;
List<String> imgTips = randomGetClickImgTips(allImages);
if (allImages < imgTips.size()) {
throw new IllegalStateException("随机生成点击图片小于请求数量, 请求生成数量=" + allImages + ",实际生成数量=" + imgTips.size());
}
for (int i = 0; i < allImages; i++) {
// 随机获取点击图片
ImgWrapper imgWrapper = getClickImg(imgTips.get(i));
BufferedImage image = imgWrapper.getImage();
int clickImgWidth = image.getWidth();
int clickImgHeight = image.getHeight();
// 随机x
int randomX;
if (i == 0) {
randomX = 1;
} else {
randomX = avg * i;
}
// 随机y
int randomY = ThreadLocalRandom.current().nextInt(10, bgImage.getHeight() - clickImgHeight);
// 通过随机x和y 进行覆盖图片
CaptchaImageUtils.overlayImage(bgImage, imgWrapper.getImage(), randomX, randomY);
ClickImageCheckDefinition clickImageCheckDefinition = new ClickImageCheckDefinition();
clickImageCheckDefinition.setTip(imgWrapper.getTip());
clickImageCheckDefinition.setX(randomX + clickImgWidth / 2);
clickImageCheckDefinition.setY(randomY + clickImgHeight / 2);
clickImageCheckDefinition.setWidth(clickImgWidth);
clickImageCheckDefinition.setHeight(clickImgHeight);
clickImageCheckDefinitionList.add(clickImageCheckDefinition);
}
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = getCheckClickImageCheckDefinitionList(clickImageCheckDefinitionList,
checkClickCount);
// wrap
return wrapClickImageCaptchaInfo(param, bgImage, checkClickImageCheckDefinitionList);
BufferedImage bgImage = getResourceImage(resourceImage);
} finally {
// 使用完后关闭流
for (InputStream inputStream : inputStreams) {
try {
inputStream.close();
} catch (IOException e) {
// ignore
}
}
List<Resource> imgTips = randomGetClickImgTips(param);
int allImages = imgTips.size();
List<ClickImageCheckDefinition> clickImageCheckDefinitionList = new ArrayList<>(allImages);
int avg = bgImage.getWidth() / allImages;
if (allImages < imgTips.size()) {
throw new IllegalStateException("随机生成点击图片小于请求数量, 请求生成数量=" + allImages + ",实际生成数量=" + imgTips.size());
}
for (int i = 0; i < allImages; i++) {
// 随机获取点击图片
ImgWrapper imgWrapper = getClickImg(imgTips.get(i));
BufferedImage image = imgWrapper.getImage();
int clickImgWidth = image.getWidth();
int clickImgHeight = image.getHeight();
// 随机x
int randomX;
if (i == 0) {
randomX = 1;
} else {
randomX = avg * i;
}
// 随机y
int randomY = randomInt(10, bgImage.getHeight() - clickImgHeight);
// 通过随机x和y 进行覆盖图片
CaptchaImageUtils.overlayImage(bgImage, imgWrapper.getImage(), randomX, randomY);
ClickImageCheckDefinition clickImageCheckDefinition = new ClickImageCheckDefinition();
clickImageCheckDefinition.setTip(imgWrapper.getTip());
clickImageCheckDefinition.setX(randomX + clickImgWidth / 2);
clickImageCheckDefinition.setY(randomY + clickImgHeight / 2);
clickImageCheckDefinition.setWidth(clickImgWidth);
clickImageCheckDefinition.setHeight(clickImgHeight);
clickImageCheckDefinitionList.add(clickImageCheckDefinition);
}
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = filterAndSortClickImageCheckDefinition(clickImageCheckDefinitionList);
// wrap
return wrapClickImageCaptchaInfo(param, bgImage, checkClickImageCheckDefinitionList, resourceImage);
}
/**
* 从总的图片中 去除要校验的图片数据 以及顺序
* 过滤并排序校验的图片点选顺序
*
* @param allCheckDefinitionList 总的点选图片
* @param checkClickCount 参与校验的数据
* @return List<ClickImageCheckDefinition>
*/
protected List<ClickImageCheckDefinition> getCheckClickImageCheckDefinitionList(List<ClickImageCheckDefinition> allCheckDefinitionList,
Integer checkClickCount) {
// 打乱
Collections.shuffle(allCheckDefinitionList);
// 拿出参与校验的数据
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = new ArrayList<>(checkClickCount);
for (int i = 0; i < checkClickCount; i++) {
ClickImageCheckDefinition clickImageCheckDefinition = allCheckDefinitionList.get(i);
checkClickImageCheckDefinitionList.add(clickImageCheckDefinition);
}
return checkClickImageCheckDefinitionList;
}
protected abstract List<ClickImageCheckDefinition> filterAndSortClickImageCheckDefinition(List<ClickImageCheckDefinition> allCheckDefinitionList);
/**
* 随机获取一组数据用于生成随机图
*
* @param tipSize tipSize
* @return List<String>
*/
protected abstract List<String> randomGetClickImgTips(int tipSize);
protected abstract List<Resource> randomGetClickImgTips(GenerateParam param);
/**
* 随机获取点击的图片
@@ -132,7 +99,7 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
* @param tip 提示数据,根据改数据生成图片
* @return ImgWrapper
*/
public abstract ImgWrapper getClickImg(String tip);
public abstract ImgWrapper getClickImg(Resource tip);
/**
* 包装 ImageCaptchaInfo
@@ -140,10 +107,11 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
* @param param param
* @param bgImage bgImage
* @param checkClickImageCheckDefinitionList checkClickImageCheckDefinitionList
* @param resourceImage 随机读取到的图片资源
* @return ImageCaptchaInfo
*/
public abstract ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList);
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList, Resource resourceImage);
/**
* @Author: 天爱有情
@@ -157,6 +125,6 @@ public abstract class AbstractClickImageCaptchaGenerator extends AbstractImageCa
/** 图片. */
private BufferedImage image;
/** 提示. */
private String tip;
private Resource tip;
}
}
@@ -171,7 +171,7 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
@Override
public ImageCaptchaInfo generateCaptchaImage(String type, String targetFormatName, String matrixFormatName) {
return generateCaptchaImage(GenerateParam.builder().type(type).backgroundFormatName(targetFormatName).sliderFormatName(matrixFormatName).build(), true);
return generateCaptchaImage(GenerateParam.builder().type(type).backgroundFormatName(targetFormatName).templateFormatName(matrixFormatName).build(), true);
}
@Override
@@ -5,6 +5,7 @@ import cloud.tianai.captcha.generator.AbstractImageCaptchaGenerator;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
@@ -55,7 +56,7 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
// 拼接验证码不需要模板 只需要背景图
Collection<InputStream> inputStreams = new LinkedList<>();
try {
Resource resourceImage = requiredRandomGetResource(param.getType());
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream);
BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream);
@@ -71,7 +72,7 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
+ bgImageTopSplit[1].getWidth(), bgImageTopSplit[0].getHeight(), bgImageTopSplit[1], bgImageTopSplit[0]);
bgImage = concatImage(false, bgImageSplit[1].getWidth(), sliderImage.getHeight() + bgImageSplit[1].getHeight(),
sliderImage, bgImageSplit[1]);
return wrapConcatCaptchaInfo(randomX, randomY, bgImage, param);
return wrapConcatCaptchaInfo(randomX, randomY, bgImage, param, resourceImage);
} finally {
// 使用完后关闭流
for (InputStream inputStream : inputStreams) {
@@ -85,9 +86,13 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
}
@SneakyThrows
private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, int randomY, BufferedImage bgImage, GenerateParam param) {
String backGroundImageBase64 = getImageTransform().transform(bgImage, param.getBackgroundFormatName());
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(backGroundImageBase64,
private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, int randomY, BufferedImage bgImage, GenerateParam param, Resource resourceImage) {
ImageTransformData transform = getImageTransform().transform(param, bgImage, resourceImage);
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(transform.getBackgroundImageUrl(),
null,
resourceImage.getTag(),
null,
bgImage.getWidth(),
bgImage.getHeight(),
@@ -6,6 +6,7 @@ import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.model.dto.ClickImageCheckDefinition;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore;
@@ -20,6 +21,7 @@ import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
@@ -47,6 +49,14 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
@Getter
@Setter
protected int tipImageInterferencePointNum = 5;
/** 参与校验的数量. */
@Getter
@Setter
protected Integer checkClickCount = 4;
/** 干扰数量. */
@Getter
@Setter
protected Integer interferenceCount = 2;
/**
* 因为在画文字图形的时候 y 值不能准确通过 除法计算得出, 字体大小不一致中间的容错值算不准确
@@ -66,14 +76,14 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
super(imageCaptchaResourceManager);
setImageTransform(imageTransform);
}
@Override
protected List<String> randomGetClickImgTips(int tipSize) {
protected List<Resource> randomGetClickImgTips(GenerateParam param) {
int tipSize = interferenceCount + checkClickCount;
ThreadLocalRandom random = ThreadLocalRandom.current();
List<String> tipList = new ArrayList<>(tipSize);
List<Resource> tipList = new ArrayList<>(tipSize);
for (int i = 0; i < tipSize; i++) {
String randomWord = FontUtils.getRandomChar(random);
tipList.add(randomWord);
tipList.add(new Resource(null, randomWord));
}
// 随机文字
return tipList;
@@ -108,7 +118,7 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
}
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining());
String tips = imageCheckDefinitions.stream().map(c -> c.getTip().getData()).collect(Collectors.joining());
// 生成随机颜色
int fontWidth = tips.length() * font.getSize();
int width = fontWidth + 6;
@@ -117,18 +127,18 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
float top = 6 / 2f + font.getSize() - currentFontTopCoef;
BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips,
font, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum);
return new ImgWrapper(bufferedImage, tips);
return new ImgWrapper(bufferedImage, new Resource(null, tips));
}
@Override
public ImgWrapper getClickImg(String tip) {
public ImgWrapper getClickImg(Resource tip) {
ThreadLocalRandom random = ThreadLocalRandom.current();
// 随机颜色
Color randomColor = CaptchaImageUtils.getRandomColor(random);
// 随机角度
int randomDeg = ThreadLocalRandom.current().nextInt(0, 85);
int randomDeg = randomInt(0, 85);
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
tip,
tip.getData(),
font,
currentFontTopCoef,
clickImgWidth,
@@ -137,19 +147,33 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
return new ImgWrapper(fontImage, tip);
}
@Override
protected List<ClickImageCheckDefinition> filterAndSortClickImageCheckDefinition(List<ClickImageCheckDefinition> allCheckDefinitionList) {
// 打乱
Collections.shuffle(allCheckDefinitionList);
// 拿出参与校验的数据
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList = new ArrayList<>(checkClickCount);
for (int i = 0; i < checkClickCount; i++) {
ClickImageCheckDefinition clickImageCheckDefinition = allCheckDefinitionList.get(i);
checkClickImageCheckDefinitionList.add(clickImageCheckDefinition);
}
return checkClickImageCheckDefinitionList;
}
@Override
public ImageCaptchaInfo wrapClickImageCaptchaInfo(GenerateParam param, BufferedImage bgImage,
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList) {
List<ClickImageCheckDefinition> checkClickImageCheckDefinitionList, Resource resourceImage) {
// 提示图片
BufferedImage tipImage = genTipImage(checkClickImageCheckDefinitionList).getImage();
ImageTransformData transform = getImageTransform().transform(param, bgImage, tipImage, resourceImage, checkClickImageCheckDefinitionList);
ImageCaptchaInfo clickImageCaptchaInfo = new ImageCaptchaInfo();
clickImageCaptchaInfo.setBackgroundImage(getImageTransform().transform(bgImage, param.getBackgroundFormatName()));
clickImageCaptchaInfo.setSliderImage(getImageTransform().transform(tipImage, param.getSliderFormatName()));
clickImageCaptchaInfo.setBgImageWidth(bgImage.getWidth());
clickImageCaptchaInfo.setBgImageHeight(bgImage.getHeight());
clickImageCaptchaInfo.setSliderImageWidth(tipImage.getWidth());
clickImageCaptchaInfo.setSliderImageHeight(tipImage.getHeight());
clickImageCaptchaInfo.setBackgroundImage(transform.getBackgroundImageUrl());
clickImageCaptchaInfo.setBackgroundImageTag(resourceImage.getTag());
clickImageCaptchaInfo.setTemplateImage(transform.getTemplateImageUrl());
clickImageCaptchaInfo.setBackgroundImageWidth(bgImage.getWidth());
clickImageCaptchaInfo.setBackgroundImageHeight(bgImage.getHeight());
clickImageCaptchaInfo.setTemplateImageWidth(tipImage.getWidth());
clickImageCaptchaInfo.setTemplateImageHeight(tipImage.getHeight());
clickImageCaptchaInfo.setRandomX(null);
clickImageCaptchaInfo.setTolerant(null);
clickImageCaptchaInfo.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
@@ -6,11 +6,13 @@ import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.generator.common.model.dto.RotateImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows;
@@ -23,6 +25,8 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
/**
* @Author: 天爱有情
* @date 2022/4/22 16:43
@@ -52,7 +56,7 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
// 添加一些系统的 模板文件
Map<String, Resource> template1 = new HashMap<>(4);
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 4);
template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png")));
template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
template1.put(SliderCaptchaConstant.TEMPLATE_MATRIX_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/matrix.png")));
@@ -62,10 +66,10 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
@Override
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
// 旋转验证码没有混淆
Map<String, Resource> templateImages = requiredRandomGetTemplate(param.getType());
ResourceMap templateImages = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
Collection<InputStream> inputStreams = new LinkedList<>();
try {
Resource resourceImage = requiredRandomGetResource(param.getType());
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream);
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
@@ -96,7 +100,7 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
int randomX = ThreadLocalRandom.current().nextInt(fixedTemplate.getWidth() + 10, targetBackground.getWidth() - 10);
double degree = 360d - randomX / ((targetBackground.getWidth()) / 360d);
CaptchaImageUtils.centerOverlayAndRotateImage(matrixTemplate, cutImage, degree);
return wrapRotateCaptchaInfo(degree, randomX, targetBackground, matrixTemplate, param);
return wrapRotateCaptchaInfo(degree, randomX, targetBackground, matrixTemplate, param, templateImages, resourceImage);
} finally {
// 使用完后关闭流
for (InputStream inputStream : inputStreams) {
@@ -110,15 +114,14 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
}
@SneakyThrows
private ImageCaptchaInfo wrapRotateCaptchaInfo(double degree, int randomX, BufferedImage backgroundImage, BufferedImage sliderImage, GenerateParam param) {
String backgroundFormatName = param.getBackgroundFormatName();
String sliderFormatName = param.getSliderFormatName();
String backGroundImageBase64 = getImageTransform().transform(backgroundImage, backgroundFormatName);
String sliderImageBase64 = getImageTransform().transform(sliderImage, sliderFormatName);
private ImageCaptchaInfo wrapRotateCaptchaInfo(double degree, int randomX, BufferedImage backgroundImage, BufferedImage sliderImage, GenerateParam param, ResourceMap templateResource, Resource resourceImage) {
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource);
return RotateImageCaptchaInfo.of(degree,
randomX,
backGroundImageBase64,
sliderImageBase64,
transform.getBackgroundImageUrl(),
transform.getTemplateImageUrl(),
resourceImage.getTag(),
templateResource.getTag(),
backgroundImage.getWidth(), backgroundImage.getHeight(),
sliderImage.getWidth(), sliderImage.getHeight()
);
@@ -6,11 +6,13 @@ import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.generator.common.model.dto.SliderImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -24,6 +26,8 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import static cloud.tianai.captcha.common.constant.CommonConstant.DEFAULT_TAG;
/**
* @Author: 天爱有情
* @Date 2020/5/29 8:06
@@ -61,10 +65,10 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
@Override
public ImageCaptchaInfo doGenerateCaptchaImage(GenerateParam param) {
Boolean obfuscate = param.getObfuscate();
Map<String, Resource> templateImages = requiredRandomGetTemplate(param.getType());
ResourceMap templateImages = requiredRandomGetTemplate(param.getType(), param.getTemplateImageTag());
Collection<InputStream> inputStreams = new LinkedList<>();
try {
Resource resourceImage = requiredRandomGetResource(param.getType());
Resource resourceImage = requiredRandomGetResource(param.getType(), param.getBackgroundImageTag());
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
inputStreams.add(resourceInputStream);
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
@@ -99,7 +103,7 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
BufferedImage cutImage = CaptchaImageUtils.cutImage(cutBackground, fixedTemplate, randomX, randomY);
CaptchaImageUtils.overlayImage(cutImage, activeTemplate, 0, 0);
CaptchaImageUtils.overlayImage(matrixTemplate, cutImage, 0, randomY);
return wrapSliderCaptchaInfo(randomX, randomY, targetBackground, matrixTemplate, param);
return wrapSliderCaptchaInfo(randomX, randomY, targetBackground, matrixTemplate, param, templateImages, resourceImage);
} finally {
// 使用完后关闭流
for (InputStream inputStream : inputStreams) {
@@ -127,14 +131,16 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
int randomY,
BufferedImage backgroundImage,
BufferedImage sliderImage,
GenerateParam param) {
String backgroundFormatName = param.getBackgroundFormatName();
String sliderFormatName = param.getSliderFormatName();
String backGroundImageBase64 = getImageTransform().transform(backgroundImage, backgroundFormatName);
String sliderImageBase64 = getImageTransform().transform(sliderImage, sliderFormatName);
GenerateParam param,
ResourceMap templateResource,
Resource resourceImage) {
ImageTransformData transform = getImageTransform().transform(param, backgroundImage, sliderImage, resourceImage, templateResource);
return SliderImageCaptchaInfo.of(randomX, randomY,
backGroundImageBase64,
sliderImageBase64,
transform.getBackgroundImageUrl(),
transform.getTemplateImageUrl(),
resourceImage.getTag(),
templateResource.getTag(),
backgroundImage.getWidth(), backgroundImage.getHeight(),
sliderImage.getWidth(), sliderImage.getHeight()
);
@@ -158,14 +164,14 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
// 添加一些系统的 模板文件
Map<String, Resource> template1 = new HashMap<>(4);
ResourceMap template1 = new ResourceMap(DEFAULT_TAG, 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(CaptchaTypeConstant.SLIDER, template1);
Map<String, Resource> template2 = new HashMap<>(4);
ResourceMap template2 = new ResourceMap(DEFAULT_TAG, 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")));
@@ -1,8 +1,11 @@
package cloud.tianai.captcha.generator.impl.transform;
import cloud.tianai.captcha.generator.ImageTransform;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageTransformData;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
import cloud.tianai.captcha.generator.common.util.ImgWriter;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.SneakyThrows;
import javax.imageio.ImageIO;
@@ -18,7 +21,6 @@ import java.util.Base64;
*/
public class Base64ImageTransform implements ImageTransform {
@Override
@SneakyThrows(IOException.class)
public String transform(BufferedImage bufferedImage, String transformType) {
// 这里判断处理一下,加一些警告日志
@@ -55,4 +57,16 @@ public class Base64ImageTransform implements ImageTransform {
// 其它的暂时不考虑
return null;
}
@Override
public ImageTransformData transform(GenerateParam param, BufferedImage backgroundImage, BufferedImage templateImage, Object backgroundResource, Object templateResource) {
ImageTransformData imageTransformData = new ImageTransformData();
if (backgroundImage != null) {
imageTransformData.setBackgroundImageUrl(transform(backgroundImage, param.getBackgroundFormatName()));
}
if (templateImage != null) {
imageTransformData.setTemplateImageUrl(transform(templateImage, param.getTemplateFormatName()));
}
return imageTransformData;
}
}
@@ -14,7 +14,7 @@ public abstract class AbstractResourceProvider implements ResourceProvider {
public InputStream getResourceInputStream(Resource data) {
InputStream resourceInputStream = doGetResourceInputStream(data);
if (resourceInputStream == null) {
throw new IllegalArgumentException("滑块验证码无法读到指定的资源[" + getName() + "]" + data);
throw new IllegalArgumentException("无法读到指定的资源[" + getName() + "]" + data);
}
return resourceInputStream;
}
@@ -1,10 +1,10 @@
package cloud.tianai.captcha.resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* @Author: 天爱有情
@@ -17,17 +17,19 @@ public interface ImageCaptchaResourceManager {
* 随机获取某个模板
*
* @param type 验证码类型
* @param tag 二级过滤,可以为空
* @return Map<String, Resource>
*/
Map<String, Resource> randomGetTemplate(String type);
ResourceMap randomGetTemplate(String type, String tag);
/**
* 随机获取某个资源对象
*
* @param type 验证码类型
* @param tag 二级过滤,可以为空
* @return Resource
*/
Resource randomGetResource(String type);
Resource randomGetResource(String type, String tag);
/**
* 获取真正的资源流通过资源对象
@@ -1,9 +1,8 @@
package cloud.tianai.captcha.resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import java.util.List;
import java.util.Map;
/**
@@ -21,55 +20,6 @@ public interface ResourceStore {
*/
void addResource(String type, Resource resource);
/**
* 清除某个类型下的所有资源
*
* @param type type
*/
void clearResources(String type);
/**
* 清除所有资源
*/
void clearAllResources();
/**
* 获取所有资源对象
*
* @return List<Resource>
*/
Map<String, List<Resource>> listAllResources();
/**
* 获取某个type下的所有资源对象
*
* @param type type
* @return List<Resource>
*/
List<Resource> listResourcesByType(String type);
/**
* 随机获取某个资源
*
* @param type type
* @return Resource
*/
Resource randomGetResource(String type);
/**
* 获取资源总数
*
* @return int
*/
int getAllResourceCount();
/**
* 获取某个type下的资源总数
*
* @param type type
* @return int
*/
int getResourceCount(String type);
/**
* 添加模板
@@ -77,34 +27,15 @@ public interface ResourceStore {
* @param type 验证码类型
* @param template 模板
*/
void addTemplate(String type, Map<String, Resource> template);
void addTemplate(String type, ResourceMap template);
/**
* 清除所有模板
*/
void clearAllTemplates();
/**
* 清除某个type下的所有模板
* 随机获取某个资源
*
* @param type type
* @return Resource
*/
void clearTemplates(String type);
/**
* 获取所有模板通过type
*
* @param 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();
Resource randomGetResourceByTypeAndTag(String type, String tag);
/**
* 随机获取某个模板通过type
@@ -112,6 +43,6 @@ public interface ResourceStore {
* @param type type
* @return Map<String, Resource>
*/
Map<String, Resource> randomGetTemplateByType(String type);
ResourceMap randomGetTemplateByTypeAndTag(String type, String tag);
}
@@ -10,10 +10,22 @@ import lombok.Data;
* @Description 资源对象
*/
@Data
@AllArgsConstructor
public class Resource {
/** 类型. */
private String type;
/** 数据,传输给 {@link ResourceProvider} 的参数 */
public String data;
/** 标签.*/
private String tag;
public Resource(String type, String data) {
this.type = type;
this.data = data;
}
public Resource(String type, String data, String tag) {
this.type = type;
this.data = data;
this.tag = tag;
}
}
@@ -0,0 +1,35 @@
package cloud.tianai.captcha.resource.common.model.dto;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.HashMap;
/**
* @Author: 天爱有情
* @date 2022/12/30 9:23
* @Description 存储一组Resource的Map, 增加tag标记
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ResourceMap extends HashMap<String, Resource> {
private String tag;
public ResourceMap(String tag) {
this.tag = tag;
}
public ResourceMap(String tag, int initialCapacity) {
super(initialCapacity);
this.tag = tag;
}
public ResourceMap(int initialCapacity) {
super(initialCapacity);
}
public ResourceMap() {
}
}
@@ -1,6 +1,7 @@
package cloud.tianai.captcha.resource.impl;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.provider.URLResourceProvider;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceProvider;
@@ -49,8 +50,8 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
}
@Override
public Map<String, Resource> randomGetTemplate(String type) {
Map<String, Resource> resourceMap = resourceStore.randomGetTemplateByType(type);
public ResourceMap randomGetTemplate(String type, String tag) {
ResourceMap resourceMap = resourceStore.randomGetTemplateByTypeAndTag(type, tag);
if (resourceMap == null) {
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
}
@@ -58,8 +59,8 @@ public class DefaultImageCaptchaResourceManager implements ImageCaptchaResourceM
}
@Override
public Resource randomGetResource(String type) {
Resource resource = resourceStore.randomGetResource(type);
public Resource randomGetResource(String type, String tag) {
Resource resource = resourceStore.randomGetResourceByTypeAndTag(type, tag);
if (resource == null) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
}
@@ -1,10 +1,10 @@
package cloud.tianai.captcha.resource.impl;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.common.util.CollectionUtils;
import cloud.tianai.captcha.common.util.ObjectUtils;
import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
@@ -15,44 +15,47 @@ import java.util.concurrent.ThreadLocalRandom;
* @Description 默认的资源存储
*/
public class DefaultResourceStore implements ResourceStore {
private static final String TYPE_TAG_SPLIT_FLAG = "|";
/**
* 模板资源.
*/
private Map<String, List<Map<String, Resource>>> templateResourceMap = new HashMap<>(2);
private Map<String, List<ResourceMap>> templateResourceMap = new HashMap<>(2);
/**
* resource.
*/
private Map<String, List<Resource>> resourceMap = new HashMap<>(2);
/** 用于检索 type和tag. */
private Map<String, List<ResourceMap>> templateResourceTagMap = new HashMap<>(2);
private Map<String, List<Resource>> resourceTagMap = new HashMap<>(2);
@Override
public void addResource(String type, Resource resource) {
resourceMap.computeIfAbsent(type, k -> new ArrayList<>(20)).add(resource);
// 添加tag标签字典
if (!ObjectUtils.isEmpty(resource.getTag())) {
resourceTagMap.computeIfAbsent(mergeTypeAndTag(type, resource.getTag()), k -> new ArrayList<>(20)).add(resource);
}
}
@Override
public void clearResources(String type) {
resourceMap.remove(type);
public void addTemplate(String type, ResourceMap template) {
templateResourceMap.computeIfAbsent(type, k -> new ArrayList<>(2)).add(template);
// 添加tag标签字典
if (!ObjectUtils.isEmpty(template.getTag())) {
templateResourceTagMap.computeIfAbsent(mergeTypeAndTag(type, template.getTag()), k -> new ArrayList<>(2)).add(template);
}
}
@Override
public void clearAllResources() {
resourceMap.clear();
}
@Override
public Map<String, List<Resource>> listAllResources() {
return resourceMap;
}
@Override
public List<Resource> listResourcesByType(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList());
}
@Override
public Resource randomGetResource(String type) {
List<Resource> resources = resourceMap.get(type);
public Resource randomGetResourceByTypeAndTag(String type, String tag) {
List<Resource> resources;
if (ObjectUtils.isEmpty(tag)) {
resources = resourceMap.get(type);
} else {
resources = resourceTagMap.get(mergeTypeAndTag(type, tag));
}
if (CollectionUtils.isEmpty(resources)) {
throw new IllegalStateException("随机获取资源错误,store中资源为空, type:" + type);
}
@@ -64,48 +67,13 @@ public class DefaultResourceStore implements ResourceStore {
}
@Override
public int getAllResourceCount() {
int count = 0;
for (List<Resource> value : resourceMap.values()) {
count += value.size();
public ResourceMap randomGetTemplateByTypeAndTag(String type, String tag) {
List<ResourceMap> templateList;
if (ObjectUtils.isEmpty(tag)) {
templateList = templateResourceMap.get(type);
} else {
templateList = templateResourceTagMap.get(mergeTypeAndTag(type, tag));
}
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);
}
@@ -117,4 +85,55 @@ public class DefaultResourceStore implements ResourceStore {
return templateList.get(randomIndex);
}
public String mergeTypeAndTag(String type, String tag) {
return type + TYPE_TAG_SPLIT_FLAG + tag;
}
public void clearResources(String type) {
resourceMap.remove(type);
}
public void clearAllResources() {
resourceMap.clear();
}
public Map<String, List<Resource>> listAllResources() {
return resourceMap;
}
public List<Resource> listResourcesByType(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList());
}
public int getAllResourceCount() {
int count = 0;
for (List<Resource> value : resourceMap.values()) {
count += value.size();
}
return count;
}
public int getResourceCount(String type) {
return resourceMap.getOrDefault(type, Collections.emptyList()).size();
}
public void clearAllTemplates() {
templateResourceMap.clear();
}
public void clearTemplates(String type) {
templateResourceMap.remove(type);
}
public List<ResourceMap> listTemplatesByType(String type) {
return templateResourceMap.getOrDefault(type, Collections.emptyList());
}
public Map<String, List<ResourceMap>> listAllTemplates() {
return templateResourceMap;
}
}
@@ -122,8 +122,8 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
ClickImageCheckDefinition definition = clickImageCheckDefinitionList.get(i);
Integer x = definition.getX();
Integer y = definition.getY();
Integer width = imageCaptchaInfo.getBgImageWidth();
Integer height = imageCaptchaInfo.getBgImageHeight();
Integer width = imageCaptchaInfo.getBackgroundImageWidth();
Integer height = imageCaptchaInfo.getBackgroundImageHeight();
float vx = calcPercentage(x, width);
float vy = calcPercentage(y, height);
sb.append(vx).append(",").append(vy).append(";");
@@ -327,8 +327,8 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
return defaultData;
}
protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> sliderCaptchaValidData) {
float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth());
sliderCaptchaValidData.put(PERCENTAGE_KEY, percentage);
protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> imageCaptchaValidData) {
float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBackgroundImageWidth());
imageCaptchaValidData.put(PERCENTAGE_KEY, percentage);
}
}