mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
优化
This commit is contained in:
@@ -306,4 +306,15 @@ public class CaptchaImageUtils {
|
|||||||
}
|
}
|
||||||
return newImage;
|
return newImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
char randomChar = getRandomChar();
|
||||||
|
System.out.println(randomChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char getRandomChar() {
|
||||||
|
return (char)(0x4e00 + (int)(Math.random()*(0x9fa5 - 0x4e00 + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package cloud.tianai.captcha.template.slider.common.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2022/4/27 11:34
|
||||||
|
* @Description 字体工具包
|
||||||
|
*/
|
||||||
|
public class FontUtils {
|
||||||
|
|
||||||
|
public static char getRandomChar() {
|
||||||
|
return (char)(0x4e00 + (int)(Math.random()*(0x9fa5 - 0x4e00 + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+2
-1
@@ -56,7 +56,8 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
|||||||
* @param formatType 格式化类型
|
* @param formatType 格式化类型
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
public String transform(BufferedImage bufferedImage, String formatType) throws IOException {
|
@SneakyThrows(IOException.class)
|
||||||
|
public String transform(BufferedImage bufferedImage, String formatType) {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
ImageIO.write(bufferedImage, formatType, byteArrayOutputStream);
|
ImageIO.write(bufferedImage, formatType, byteArrayOutputStream);
|
||||||
//转换成字节码
|
//转换成字节码
|
||||||
|
|||||||
+2
@@ -13,4 +13,6 @@ public interface CaptchaTypeConstant {
|
|||||||
String ROTATE = "ROTATE";
|
String ROTATE = "ROTATE";
|
||||||
/** 拼接.*/
|
/** 拼接.*/
|
||||||
String CONCAT = "CONCAT";
|
String CONCAT = "CONCAT";
|
||||||
|
/** 文字点选.*/
|
||||||
|
String WORD_CLICK = "WORD_CLICK";
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -30,8 +30,12 @@ public class ImageCaptchaInfo {
|
|||||||
private Integer sliderImageWidth;
|
private Integer sliderImageWidth;
|
||||||
/** 滑块图片高度. */
|
/** 滑块图片高度. */
|
||||||
private Integer sliderImageHeight;
|
private Integer sliderImageHeight;
|
||||||
/** 随机值.*/
|
/** 随机值. */
|
||||||
private Integer randomX;
|
private Integer randomX;
|
||||||
|
/** 容错值, 可以为空 默认 0.02容错,校验的时候用. */
|
||||||
|
private Float tolerant;
|
||||||
|
/** 验证码类型.*/
|
||||||
|
private String type;
|
||||||
/**
|
/**
|
||||||
* 扩展字段
|
* 扩展字段
|
||||||
*/
|
*/
|
||||||
@@ -43,7 +47,8 @@ public class ImageCaptchaInfo {
|
|||||||
Integer bgImageHeight,
|
Integer bgImageHeight,
|
||||||
Integer sliderImageWidth,
|
Integer sliderImageWidth,
|
||||||
Integer sliderImageHeight,
|
Integer sliderImageHeight,
|
||||||
Integer randomX) {
|
Integer randomX,
|
||||||
|
String type) {
|
||||||
this.backgroundImage = backgroundImage;
|
this.backgroundImage = backgroundImage;
|
||||||
this.sliderImage = sliderImage;
|
this.sliderImage = sliderImage;
|
||||||
this.bgImageWidth = bgImageWidth;
|
this.bgImageWidth = bgImageWidth;
|
||||||
@@ -51,6 +56,7 @@ public class ImageCaptchaInfo {
|
|||||||
this.sliderImageWidth = sliderImageWidth;
|
this.sliderImageWidth = sliderImageWidth;
|
||||||
this.sliderImageHeight = sliderImageHeight;
|
this.sliderImageHeight = sliderImageHeight;
|
||||||
this.randomX = randomX;
|
this.randomX = randomX;
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageCaptchaInfo of(String backgroundImage,
|
public static ImageCaptchaInfo of(String backgroundImage,
|
||||||
@@ -59,8 +65,9 @@ public class ImageCaptchaInfo {
|
|||||||
Integer bgImageHeight,
|
Integer bgImageHeight,
|
||||||
Integer sliderImageWidth,
|
Integer sliderImageWidth,
|
||||||
Integer sliderImageHeight,
|
Integer sliderImageHeight,
|
||||||
Integer randomKey) {
|
Integer randomKey,
|
||||||
return new ImageCaptchaInfo( backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey);
|
String type) {
|
||||||
|
return new ImageCaptchaInfo(backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -1,5 +1,6 @@
|
|||||||
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
|
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
@@ -17,8 +18,8 @@ public class RotateImageCaptchaInfo extends ImageCaptchaInfo {
|
|||||||
* 旋转多少度
|
* 旋转多少度
|
||||||
*/
|
*/
|
||||||
private Double degree;
|
private Double degree;
|
||||||
|
/** 旋转图片的容错值大一点. */
|
||||||
|
public static final Float DEFAULT_TOLERANT = 0.05F;
|
||||||
|
|
||||||
public static RotateImageCaptchaInfo of(Double degree,
|
public static RotateImageCaptchaInfo of(Double degree,
|
||||||
Integer randomX,
|
Integer randomX,
|
||||||
@@ -32,11 +33,14 @@ public class RotateImageCaptchaInfo extends ImageCaptchaInfo {
|
|||||||
rotateImageCaptchaInfo.setDegree(degree);
|
rotateImageCaptchaInfo.setDegree(degree);
|
||||||
rotateImageCaptchaInfo.setRandomX(randomX);
|
rotateImageCaptchaInfo.setRandomX(randomX);
|
||||||
rotateImageCaptchaInfo.setBackgroundImage(backgroundImage);
|
rotateImageCaptchaInfo.setBackgroundImage(backgroundImage);
|
||||||
|
rotateImageCaptchaInfo.setTolerant(DEFAULT_TOLERANT);
|
||||||
rotateImageCaptchaInfo.setSliderImage(sliderImage);
|
rotateImageCaptchaInfo.setSliderImage(sliderImage);
|
||||||
rotateImageCaptchaInfo.setBgImageWidth(bgImageWidth);
|
rotateImageCaptchaInfo.setBgImageWidth(bgImageWidth);
|
||||||
rotateImageCaptchaInfo.setBgImageHeight(bgImageHeight);
|
rotateImageCaptchaInfo.setBgImageHeight(bgImageHeight);
|
||||||
rotateImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
|
rotateImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
|
||||||
rotateImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
|
rotateImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
|
||||||
|
// 类型为旋转图片验证码
|
||||||
|
rotateImageCaptchaInfo.setType(CaptchaTypeConstant.ROTATE);
|
||||||
return rotateImageCaptchaInfo;
|
return rotateImageCaptchaInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
|
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
@@ -36,7 +37,7 @@ public class SliderImageCaptchaInfo extends ImageCaptchaInfo {
|
|||||||
sliderImageCaptchaInfo.setBgImageHeight(bgImageHeight);
|
sliderImageCaptchaInfo.setBgImageHeight(bgImageHeight);
|
||||||
sliderImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
|
sliderImageCaptchaInfo.setSliderImageWidth(sliderImageWidth);
|
||||||
sliderImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
|
sliderImageCaptchaInfo.setSliderImageHeight(sliderImageHeight);
|
||||||
|
sliderImageCaptchaInfo.setType(CaptchaTypeConstant.SLIDER);
|
||||||
return sliderImageCaptchaInfo;
|
return sliderImageCaptchaInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package cloud.tianai.captcha.template.slider.generator.common.model.dto;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2022/4/27 15:33
|
||||||
|
* @Description 文字点选
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class WordClickImageCaptchaInfo extends ImageCaptchaInfo {
|
||||||
|
|
||||||
|
private List<WordDefinition> checkWords;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class WordDefinition {
|
||||||
|
private String word;
|
||||||
|
private Integer x;
|
||||||
|
private Integer y;
|
||||||
|
private Integer deg;
|
||||||
|
private Integer wordWidth;
|
||||||
|
private Integer wordHeight;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+217
@@ -0,0 +1,217 @@
|
|||||||
|
package cloud.tianai.captcha.template.slider.generator.impl;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils;
|
||||||
|
import cloud.tianai.captcha.template.slider.common.util.FontUtils;
|
||||||
|
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.model.dto.GenerateParam;
|
||||||
|
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
|
||||||
|
import cloud.tianai.captcha.template.slider.generator.common.model.dto.WordClickImageCaptchaInfo;
|
||||||
|
import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager;
|
||||||
|
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.impl.provider.ClassPathResourceProvider;
|
||||||
|
import cloud.tianai.captcha.template.slider.resource.impl.provider.FileResourceProvider;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import sun.font.FontDesignMetrics;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.wrapFile2BufferedImage;
|
||||||
|
import static cloud.tianai.captcha.template.slider.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2022/4/27 11:46
|
||||||
|
* @Description 点选验证码
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class StandardClickImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||||
|
|
||||||
|
|
||||||
|
protected ImageCaptchaResourceManager imageCaptchaResourceManager;
|
||||||
|
|
||||||
|
protected Integer checkFontCount = 4;
|
||||||
|
protected Integer interferenceCount = checkFontCount + 2;
|
||||||
|
protected Font font;
|
||||||
|
protected List<Color> randomColors = Arrays.asList(Color.PINK, Color.BLUE, Color.GREEN, Color.BLACK);
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public StandardClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
|
||||||
|
this.imageCaptchaResourceManager = imageCaptchaResourceManager;
|
||||||
|
if (initDefaultResource) {
|
||||||
|
initDefaultResource();
|
||||||
|
}
|
||||||
|
Resource fontResource = new Resource("", "META-INF/fonts/SIMSUN.TTC");
|
||||||
|
InputStream inputStream = new ClassPathResourceProvider().doGetResourceInputStream(fontResource);
|
||||||
|
this.font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
|
||||||
|
this.font = font.deriveFont(Font.BOLD, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager,
|
||||||
|
boolean initDefaultResource,
|
||||||
|
Font font) {
|
||||||
|
this.imageCaptchaResourceManager = imageCaptchaResourceManager;
|
||||||
|
this.font = font;
|
||||||
|
if (initDefaultResource) {
|
||||||
|
initDefaultResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void initDefaultResource() {
|
||||||
|
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||||
|
// 添加一些系统的资源文件
|
||||||
|
resourceStore.addResource(CaptchaTypeConstant.WORD_CLICK, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) {
|
||||||
|
|
||||||
|
// 文字点选验证码不需要模板 只需要背景图
|
||||||
|
Collection<InputStream> inputStreams = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
Resource resourceImage = imageCaptchaResourceManager.randomGetResource(param.getType());
|
||||||
|
InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage);
|
||||||
|
inputStreams.add(resourceInputStream);
|
||||||
|
BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream);
|
||||||
|
Graphics2D graphics = (Graphics2D) bgImage.getGraphics();
|
||||||
|
|
||||||
|
List<WordClickImageCaptchaInfo.WordDefinition> wordDefinitionList = new ArrayList<>(interferenceCount);
|
||||||
|
for (int i = 0; i < interferenceCount; i++) {
|
||||||
|
// 随机角度
|
||||||
|
|
||||||
|
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
|
||||||
|
// 随机文字
|
||||||
|
String randomWord = String.valueOf(FontUtils.getRandomChar());
|
||||||
|
int wordWidth = metrics.stringWidth(randomWord);
|
||||||
|
int wordHeight = metrics.getHeight();
|
||||||
|
// 随机颜色
|
||||||
|
Color randomColor = randomColor();
|
||||||
|
// 随机x
|
||||||
|
int randomX = ThreadLocalRandom.current().nextInt(10, bgImage.getWidth() - wordWidth);
|
||||||
|
// 随机y
|
||||||
|
int randomY = ThreadLocalRandom.current().nextInt(10, bgImage.getHeight() - wordHeight);
|
||||||
|
int randomDeg = ThreadLocalRandom.current().nextInt(0, 85);
|
||||||
|
AffineTransform affineTransform = new AffineTransform();
|
||||||
|
affineTransform.rotate(Math.toRadians(randomDeg));
|
||||||
|
Font rotatedFont = font.deriveFont(affineTransform);
|
||||||
|
graphics.setFont(rotatedFont);
|
||||||
|
graphics.setColor(randomColor);
|
||||||
|
graphics.drawString(randomWord, randomX, randomY + metrics.getAscent());
|
||||||
|
|
||||||
|
WordClickImageCaptchaInfo.WordDefinition wordDefinition = new WordClickImageCaptchaInfo.WordDefinition();
|
||||||
|
wordDefinition.setWord(randomWord);
|
||||||
|
wordDefinition.setX(randomX + wordWidth / 2);
|
||||||
|
wordDefinition.setY(randomY + wordHeight / 2);
|
||||||
|
wordDefinition.setDeg(randomDeg);
|
||||||
|
wordDefinition.setWordWidth(wordWidth);
|
||||||
|
wordDefinition.setWordHeight(wordHeight);
|
||||||
|
wordDefinitionList.add(wordDefinition);
|
||||||
|
}
|
||||||
|
// 背景图转换为字符串
|
||||||
|
// String bgImageStr = transform(bgImage, param.getBackgroundFormatName());
|
||||||
|
try {
|
||||||
|
ImageIO.write(bgImage, "jpeg", new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\123.jpg"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打乱 word
|
||||||
|
Collections.shuffle(wordDefinitionList);
|
||||||
|
// 拿出要校验的文字
|
||||||
|
List<WordClickImageCaptchaInfo.WordDefinition> checkWordDefinitionList = new ArrayList<>(checkFontCount);
|
||||||
|
for (int i = 0; i < checkFontCount; i++) {
|
||||||
|
checkWordDefinitionList.add(wordDefinitionList.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(checkWordDefinitionList);
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 使用完后关闭流
|
||||||
|
for (InputStream inputStream : inputStreams) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color randomColor() {
|
||||||
|
if (randomColors.size() == 1) {
|
||||||
|
return randomColors.get(0);
|
||||||
|
}
|
||||||
|
return randomColors.get(ThreadLocalRandom.current().nextInt(0, randomColors.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageCaptchaResourceManager getImageResourceManager() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException, FontFormatException {
|
||||||
|
Resource fontResource = new Resource("", "META-INF/fonts/SIMSUN.TTC");
|
||||||
|
InputStream inputStream = new ClassPathResourceProvider().doGetResourceInputStream(fontResource);
|
||||||
|
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
|
||||||
|
font = font.deriveFont(Font.BOLD, 100);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 40; i < 60; i+=1) {
|
||||||
|
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
|
||||||
|
|
||||||
|
Resource imageResource = new Resource("", "C:\\Users\\Thinkpad\\Desktop\\111.jpg");
|
||||||
|
inputStream = new FileResourceProvider().doGetResourceInputStream(imageResource);
|
||||||
|
BufferedImage bufferedImage = CaptchaImageUtils.wrapFile2BufferedImage(inputStream);
|
||||||
|
|
||||||
|
Graphics graphics = bufferedImage.getGraphics();
|
||||||
|
graphics.setColor(Color.PINK);
|
||||||
|
//设置角度
|
||||||
|
AffineTransform affineTransform = new AffineTransform();
|
||||||
|
affineTransform.rotate(Math.toRadians(180), i , -i);
|
||||||
|
Font rotatedFont = font.deriveFont(affineTransform);
|
||||||
|
graphics.setFont(rotatedFont);
|
||||||
|
char ch = FontUtils.getRandomChar();
|
||||||
|
// graphics.drawString(String.valueOf(ch), 10, 10+metrics.getAscent());
|
||||||
|
// graphics.drawString(String.valueOf(ch), 50, 50);
|
||||||
|
String randomWord = "张"/*String.valueOf(FontUtils.getRandomChar())*/;
|
||||||
|
int wordWidth = metrics.stringWidth(randomWord);
|
||||||
|
int wordHeight = metrics.getHeight();
|
||||||
|
// //左边位置
|
||||||
|
int left = (bufferedImage.getWidth()-wordWidth)/2;
|
||||||
|
// //顶边位置+上升距离(原本字体基线位置对准画布的y坐标导致字体偏上ascent距离,加上ascent后下移刚好顶边吻合)
|
||||||
|
int top = (bufferedImage.getHeight()-wordHeight)/2+metrics.getAscent();
|
||||||
|
|
||||||
|
// 随机生成6个字的居中的 x 和 y
|
||||||
|
// for (int i = 0; i < 6; i++) {
|
||||||
|
|
||||||
|
|
||||||
|
// int randomX = ThreadLocalRandom.current().nextInt(10, bufferedImage.getWidth() - wordWidth);
|
||||||
|
// int randomY = ThreadLocalRandom.current().nextInt(10, bufferedImage.getHeight() - wordHeight);
|
||||||
|
|
||||||
|
graphics.drawString(randomWord, left, top);
|
||||||
|
|
||||||
|
System.out.println(randomWord + "-->x:" + (left + wordWidth / 2) + ",y:" + (left + wordHeight / 2));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// graphics.drawString("居中文字",0,metrics.getAscent()); //基线对齐改为顶边对齐
|
||||||
|
ImageIO.write(bufferedImage, "jpeg", new FileOutputStream("C:\\Users\\Thinkpad\\Desktop\\111\\"+i+".jpg"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-2
@@ -27,7 +27,7 @@ import static cloud.tianai.captcha.template.slider.generator.impl.StandardSlider
|
|||||||
*/
|
*/
|
||||||
public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGenerator {
|
||||||
|
|
||||||
protected final ImageCaptchaResourceManager imageCaptchaResourceManager;
|
protected ImageCaptchaResourceManager imageCaptchaResourceManager;
|
||||||
|
|
||||||
public StandardConcatImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
|
public StandardConcatImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
|
||||||
this.imageCaptchaResourceManager = imageCaptchaResourceManager;
|
this.imageCaptchaResourceManager = imageCaptchaResourceManager;
|
||||||
@@ -79,7 +79,16 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, BufferedImage bgImage, BufferedImage sliderImage, GenerateParam param) {
|
private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, BufferedImage bgImage, BufferedImage sliderImage, GenerateParam param) {
|
||||||
String backGroundImageBase64 = transform(bgImage, param.getBackgroundFormatName());
|
String backGroundImageBase64 = transform(bgImage, param.getBackgroundFormatName());
|
||||||
String sliderImageBase64 = transform(sliderImage, param.getSliderFormatName());
|
String sliderImageBase64 = transform(sliderImage, param.getSliderFormatName());
|
||||||
return ImageCaptchaInfo.of(backGroundImageBase64, sliderImageBase64, bgImage.getWidth(), bgImage.getHeight(), sliderImage.getWidth(), sliderImage.getHeight(), randomX);
|
ImageCaptchaInfo imageCaptchaInfo = ImageCaptchaInfo.of(backGroundImageBase64,
|
||||||
|
sliderImageBase64,
|
||||||
|
bgImage.getWidth(),
|
||||||
|
bgImage.getHeight(),
|
||||||
|
sliderImage.getWidth(),
|
||||||
|
sliderImage.getHeight(),
|
||||||
|
randomX,
|
||||||
|
CaptchaTypeConstant.CONCAT);
|
||||||
|
imageCaptchaInfo.setTolerant(0.05F);
|
||||||
|
return imageCaptchaInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+79
-22
@@ -1,6 +1,8 @@
|
|||||||
package cloud.tianai.captcha.template.slider.validator.impl;
|
package cloud.tianai.captcha.template.slider.validator.impl;
|
||||||
|
|
||||||
import cloud.tianai.captcha.template.slider.common.util.CollectionUtils;
|
import cloud.tianai.captcha.template.slider.common.util.CollectionUtils;
|
||||||
|
import cloud.tianai.captcha.template.slider.common.util.ObjectUtils;
|
||||||
|
import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant;
|
||||||
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
|
import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo;
|
||||||
import cloud.tianai.captcha.template.slider.validator.ImageCaptchaValidator;
|
import cloud.tianai.captcha.template.slider.validator.ImageCaptchaValidator;
|
||||||
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
|
import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack;
|
||||||
@@ -26,6 +28,10 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
|||||||
@Setter
|
@Setter
|
||||||
public float defaultTolerant = DEFAULT_TOLERANT;
|
public float defaultTolerant = DEFAULT_TOLERANT;
|
||||||
|
|
||||||
|
public static final String PERCENTAGE_KEY = "percentage";
|
||||||
|
public static final String TOLERANT_KEY = "Tolerant";
|
||||||
|
public static final String TYPE_KEY = "type";
|
||||||
|
|
||||||
public SimpleImageCaptchaValidator() {
|
public SimpleImageCaptchaValidator() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,16 +65,28 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
|||||||
public Map<String, Object> generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
|
public Map<String, Object> generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) {
|
||||||
Map<String, Object> map = new HashMap<>(8);
|
Map<String, Object> map = new HashMap<>(8);
|
||||||
addPercentage(imageCaptchaInfo, map);
|
addPercentage(imageCaptchaInfo, map);
|
||||||
|
// 容错值
|
||||||
|
Float tolerant = imageCaptchaInfo.getTolerant();
|
||||||
|
if (tolerant != null && tolerant > 0) {
|
||||||
|
map.put(TOLERANT_KEY, tolerant);
|
||||||
|
}
|
||||||
|
// 类型
|
||||||
|
String type = imageCaptchaInfo.getType();
|
||||||
|
if (ObjectUtils.isEmpty(type)) {
|
||||||
|
type = CaptchaTypeConstant.SLIDER;
|
||||||
|
}
|
||||||
|
map.put(TYPE_KEY, type);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData) {
|
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData) {
|
||||||
Float oriPercentage = getPercentage(sliderCaptchaTrack, sliderCaptchaValidData);
|
Float oriPercentage = getFloatParam(PERCENTAGE_KEY, sliderCaptchaValidData);
|
||||||
if (oriPercentage == null) {
|
// 读容错值
|
||||||
// 没读取到百分比
|
Float tolerantData = getFloatParam(TOLERANT_KEY, sliderCaptchaValidData, defaultTolerant);
|
||||||
return false;
|
// 读验证码类型
|
||||||
}
|
String type = getStringParam(TYPE_KEY, sliderCaptchaValidData, CaptchaTypeConstant.SLIDER);
|
||||||
|
|
||||||
Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
|
Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
|
||||||
if (bgImageWidth == null || bgImageWidth < 1) {
|
if (bgImageWidth == null || bgImageWidth < 1) {
|
||||||
// 没有背景图片宽度
|
// 没有背景图片宽度
|
||||||
@@ -79,35 +97,74 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
|||||||
// 没有滑动轨迹
|
// 没有滑动轨迹
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 取最后一个滑动轨迹
|
return doValid(sliderCaptchaTrack, sliderCaptchaValidData, oriPercentage, tolerantData, type);
|
||||||
SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
|
|
||||||
// 计算百分比
|
|
||||||
float calcPercentage = calcPercentage(lastTrack.getX(), bgImageWidth);
|
|
||||||
// 校验百分比
|
|
||||||
return checkPercentage(calcPercentage, oriPercentage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Float getPercentage(SliderCaptchaTrack sliderCaptchaTrack, Map<String, Object> sliderCaptchaValidData) {
|
public boolean doValid(SliderCaptchaTrack sliderCaptchaTrack,
|
||||||
Object percentage = sliderCaptchaValidData.get("percentage");
|
Map<String, Object> sliderCaptchaValidData,
|
||||||
if (percentage != null) {
|
Float oriPercentage,
|
||||||
if (percentage instanceof Number) {
|
Float tolerant,
|
||||||
return ((Number) percentage).floatValue();
|
String type) {
|
||||||
|
if (CaptchaTypeConstant.SLIDER.equals(type)
|
||||||
|
|| CaptchaTypeConstant.ROTATE.equals(type)
|
||||||
|
|| CaptchaTypeConstant.CONCAT.equals(type)) {
|
||||||
|
if (oriPercentage == null) {
|
||||||
|
// 没读取到百分比
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList();
|
||||||
|
// 取最后一个滑动轨迹
|
||||||
|
SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
|
||||||
|
// 计算百分比
|
||||||
|
float calcPercentage = calcPercentage(lastTrack.getX(), sliderCaptchaTrack.getBgImageWidth());
|
||||||
|
// 校验百分比
|
||||||
|
return checkPercentage(calcPercentage, oriPercentage, tolerant);
|
||||||
|
}
|
||||||
|
// 不支持的类型
|
||||||
|
log.warn("校验验证码警告, 不支持的验证码类型:{}, 请手动扩展 cloud.tianai.captcha.template.slider.validator.impl.SimpleImageCaptchaValidator.doValid 进行校验扩展", type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Float getFloatParam(String key, Map<String, Object> sliderCaptchaValidData) {
|
||||||
|
return getFloatParam(key, sliderCaptchaValidData, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Float getFloatParam(String key, Map<String, Object> sliderCaptchaValidData, Float defaultData) {
|
||||||
|
Object data = sliderCaptchaValidData.get(key);
|
||||||
|
if (data != null) {
|
||||||
|
if (data instanceof Number) {
|
||||||
|
return ((Number) data).floatValue();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (percentage instanceof String) {
|
if (data instanceof String) {
|
||||||
return Float.parseFloat((String) percentage);
|
return Float.parseFloat((String) data);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
log.error("从 sliderCaptchaValidData 读取到的 percentage无法转换成float类型, [{}]", percentage);
|
log.error("从 sliderCaptchaValidData 读取到的 " + key + "无法转换成float类型, [{}]", data);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.warn("无法从 sliderCaptchaValidData 获取到 percentage");
|
return defaultData;
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
protected String getStringParam(String key, Map<String, Object> sliderCaptchaValidData, String defaultData) {
|
||||||
|
Object data = sliderCaptchaValidData.get(key);
|
||||||
|
if (data != null) {
|
||||||
|
if (data instanceof String) {
|
||||||
|
return (String) data;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return String.valueOf(data);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("从 sliderCaptchaValidData 读取到的 " + key + "无法转换成String类型, [{}]", data);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultData;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> sliderCaptchaValidData) {
|
protected void addPercentage(ImageCaptchaInfo imageCaptchaInfo, Map<String, Object> sliderCaptchaValidData) {
|
||||||
float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth());
|
float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth());
|
||||||
sliderCaptchaValidData.put("percentage", percentage);
|
sliderCaptchaValidData.put(PERCENTAGE_KEY, percentage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
|||||||
|
package example;
|
||||||
|
|
||||||
|
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.ImageCaptchaInfo;
|
||||||
|
import cloud.tianai.captcha.template.slider.generator.impl.StandardClickImageCaptchaGenerator;
|
||||||
|
import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager;
|
||||||
|
import cloud.tianai.captcha.template.slider.resource.impl.DefaultImageCaptchaResourceManager;
|
||||||
|
|
||||||
|
public class StandardWordClickImageCaptchaGeneratorTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||||
|
|
||||||
|
StandardClickImageCaptchaGenerator defaultImageCaptchaResourceManager =
|
||||||
|
new StandardClickImageCaptchaGenerator(imageCaptchaResourceManager, true);
|
||||||
|
|
||||||
|
GenerateParam generateParam = new GenerateParam();
|
||||||
|
generateParam.setType(CaptchaTypeConstant.WORD_CLICK);
|
||||||
|
ImageCaptchaInfo imageCaptchaInfo = defaultImageCaptchaResourceManager.generateCaptchaImage(generateParam);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user