mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-24 19:32:43 +08:00
U 解决 java17默认不支持 FontDesignMetrics 类的问题, 故剔除该类 使用其它算法替代
This commit is contained in:
@@ -57,13 +57,15 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
|||||||
if (init) {
|
if (init) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
init = true;
|
||||||
try {
|
try {
|
||||||
|
log.info("图片验证码[{}]初始化...", this.getClass().getSimpleName());
|
||||||
doInit();
|
doInit();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
init = false;
|
||||||
log.error("[{}]初始化失败,ex", this.getClass().getSimpleName(), e);
|
log.error("[{}]初始化失败,ex", this.getClass().getSimpleName(), e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
init = true;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ public class CaptchaImageUtils {
|
|||||||
public static BufferedImage drawWordImg(Color fontColor,
|
public static BufferedImage drawWordImg(Color fontColor,
|
||||||
String word,
|
String word,
|
||||||
Font font,
|
Font font,
|
||||||
FontDesignMetrics metrics,
|
float fontTopCoef,
|
||||||
int imgWidth,
|
int imgWidth,
|
||||||
int imgHeight,
|
int imgHeight,
|
||||||
float deg) {
|
float deg) {
|
||||||
@@ -333,7 +333,7 @@ public class CaptchaImageUtils {
|
|||||||
g.setColor(fontColor);
|
g.setColor(fontColor);
|
||||||
g.setFont(font);
|
g.setFont(font);
|
||||||
float left = (imgWidth - font.getSize()) / 2f;
|
float left = (imgWidth - font.getSize()) / 2f;
|
||||||
float top = (imgHeight - font.getSize()) / 2f + metrics.getAscent() - 6;
|
float top = (imgHeight - font.getSize()) / 2f + font.getSize() - fontTopCoef;
|
||||||
g.rotate(Math.toRadians(deg), imgWidth / 2f, imgHeight / 2f);
|
g.rotate(Math.toRadians(deg), imgWidth / 2f, imgHeight / 2f);
|
||||||
g.drawString(word, left, top);
|
g.drawString(word, left, top);
|
||||||
g.dispose();
|
g.dispose();
|
||||||
@@ -405,7 +405,6 @@ public class CaptchaImageUtils {
|
|||||||
*
|
*
|
||||||
* @param data 验证码内容
|
* @param data 验证码内容
|
||||||
* @param font 字体包
|
* @param font 字体包
|
||||||
* @param metrics FontDesignMetrics
|
|
||||||
* @param width 验证码宽度
|
* @param width 验证码宽度
|
||||||
* @param height 验证码高度
|
* @param height 验证码高度
|
||||||
* @param startX 起始X
|
* @param startX 起始X
|
||||||
@@ -416,7 +415,6 @@ public class CaptchaImageUtils {
|
|||||||
*/
|
*/
|
||||||
public static BufferedImage genSimpleImgCaptcha(String data,
|
public static BufferedImage genSimpleImgCaptcha(String data,
|
||||||
Font font,
|
Font font,
|
||||||
FontDesignMetrics metrics,
|
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
float startX,
|
float startX,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cloud.tianai.captcha.generator.impl;
|
package cloud.tianai.captcha.generator.impl;
|
||||||
|
|
||||||
import cloud.tianai.captcha.common.util.NamedThreadFactory;
|
|
||||||
import cloud.tianai.captcha.common.util.NamedThreadFactory;
|
import cloud.tianai.captcha.common.util.NamedThreadFactory;
|
||||||
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
||||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
||||||
@@ -69,8 +68,7 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
|
|||||||
private void init(int z) {
|
private void init(int z) {
|
||||||
this.size = z;
|
this.size = z;
|
||||||
// 初始化一个队列扫描
|
// 初始化一个队列扫描
|
||||||
scheduledExecutor.scheduleAtFixedRate(() -> {
|
scheduledExecutor.scheduleAtFixedRate(() -> queueMap.forEach((k, queue) -> {
|
||||||
queueMap.forEach((k, queue) -> {
|
|
||||||
try {
|
try {
|
||||||
AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0));
|
AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0));
|
||||||
int addCount = 0;
|
int addCount = 0;
|
||||||
@@ -109,9 +107,7 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
|
|||||||
// 休眠
|
// 休眠
|
||||||
sleep();
|
sleep();
|
||||||
}
|
}
|
||||||
});
|
}), 0, period, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
}, 0, period, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sleep() {
|
private void sleep() {
|
||||||
@@ -121,6 +117,11 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageCaptchaGenerator init() {
|
||||||
|
return target.init();
|
||||||
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public ImageCaptchaInfo generateCaptchaImage(String type) {
|
public ImageCaptchaInfo generateCaptchaImage(String type) {
|
||||||
@@ -176,20 +177,4 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
|
|||||||
public ImageCaptchaResourceManager getImageResourceManager() {
|
public ImageCaptchaResourceManager getImageResourceManager() {
|
||||||
return target.getImageResourceManager();
|
return target.getImageResourceManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// public static void main(String[] args) throws InterruptedException {
|
|
||||||
// SliderCaptchaTemplate captchaTemplate = new DefaultSliderCaptchaTemplate("jpeg", "png", true);
|
|
||||||
//
|
|
||||||
// captchaTemplate = new CacheSliderCaptchaTemplate(captchaTemplate, 20);
|
|
||||||
// TimeUnit.SECONDS.sleep(5);
|
|
||||||
// for (int i = 0; i < 100; i++) {
|
|
||||||
// long start = System.currentTimeMillis();
|
|
||||||
// SliderCaptchaInfo info = captchaTemplate.getSlideImageInfo();
|
|
||||||
// long end = System.currentTimeMillis();
|
|
||||||
// System.out.println("耗时:" + (end - start));
|
|
||||||
// TimeUnit.MILLISECONDS.sleep(10);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-11
@@ -13,10 +13,11 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import sun.font.FontDesignMetrics;
|
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -36,9 +37,6 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
|||||||
protected Font font;
|
protected Font font;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
protected FontDesignMetrics metrics;
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
protected Integer clickImgWidth = 80;
|
protected Integer clickImgWidth = 80;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@@ -50,6 +48,16 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
|||||||
@Setter
|
@Setter
|
||||||
protected int tipImageInterferencePointNum = 5;
|
protected int tipImageInterferencePointNum = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 因为在画文字图形的时候 y 值不能准确通过 除法计算得出, 字体大小不一致中间的容错值不可估计,
|
||||||
|
* 所以通过 线性回归模型 计算出 intercept和coef 用于计算 容错值
|
||||||
|
* 训练数据为 宋体 字体大小为 30~150 随机选择7组数据进行训练, 训练后r2结果为 0.9967106324620846
|
||||||
|
*/
|
||||||
|
protected float intercept = 0.39583333f;
|
||||||
|
protected float coef = 0.14645833f;
|
||||||
|
|
||||||
|
protected float currentFontTopCoef = 0.0f;
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
|
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
|
||||||
super(imageCaptchaResourceManager, initDefaultResource);
|
super(imageCaptchaResourceManager, initDefaultResource);
|
||||||
@@ -67,7 +75,8 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
|||||||
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
|
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
|
||||||
this.font = font.deriveFont(Font.BOLD, 70);
|
this.font = font.deriveFont(Font.BOLD, 70);
|
||||||
}
|
}
|
||||||
this.metrics = FontDesignMetrics.getMetrics(font);
|
// 计算容错
|
||||||
|
currentFontTopCoef = coef * font.getSize() + intercept;
|
||||||
if (initDefaultResource) {
|
if (initDefaultResource) {
|
||||||
initDefaultResource();
|
initDefaultResource();
|
||||||
}
|
}
|
||||||
@@ -88,20 +97,25 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
|||||||
resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
|
resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
|
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
|
||||||
String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining());
|
String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining());
|
||||||
// 生成随机颜色
|
// 生成随机颜色
|
||||||
int fontWidth = metrics.stringWidth(tips);
|
int fontWidth = tips.length() * font.getSize();
|
||||||
int width = fontWidth + 5;
|
int width = fontWidth + 6;
|
||||||
int height = metrics.getHeight() + 5;
|
int height = font.getSize() + 6;
|
||||||
float left = (width - fontWidth) / 2f;
|
float left = (width - fontWidth) / 2f;
|
||||||
float top = 5 / 2f + metrics.getAscent();
|
float top = 6 / 2f + font.getSize() - currentFontTopCoef;
|
||||||
BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips,
|
BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips,
|
||||||
font, metrics, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum);
|
font, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum);
|
||||||
|
FileOutputStream fileOutputStream = new FileOutputStream("d:/aaa/" + tips + ".png");
|
||||||
|
ImageIO.write(bufferedImage, "png", fileOutputStream);
|
||||||
|
fileOutputStream.close();
|
||||||
return new ImgWrapper(bufferedImage, tips);
|
return new ImgWrapper(bufferedImage, tips);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public ImgWrapper randomGetClickImg() {
|
public ImgWrapper randomGetClickImg() {
|
||||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||||
@@ -114,10 +128,13 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
|
|||||||
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
|
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
|
||||||
randomWord,
|
randomWord,
|
||||||
font,
|
font,
|
||||||
this.metrics,
|
currentFontTopCoef,
|
||||||
clickImgWidth,
|
clickImgWidth,
|
||||||
clickImgHeight,
|
clickImgHeight,
|
||||||
randomDeg);
|
randomDeg);
|
||||||
|
FileOutputStream fileOutputStream = new FileOutputStream("d:/aaa/" + randomWord + ".png");
|
||||||
|
ImageIO.write(fontImage, "png", fileOutputStream);
|
||||||
|
fileOutputStream.close();
|
||||||
return new ImgWrapper(fontImage, randomWord);
|
return new ImgWrapper(fontImage, randomWord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package example;
|
package example;
|
||||||
|
|
||||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||||
|
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
||||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
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.ImageCaptchaInfo;
|
||||||
import cloud.tianai.captcha.generator.impl.StandardRandomWordClickImageCaptchaGenerator;
|
import cloud.tianai.captcha.generator.impl.StandardRandomWordClickImageCaptchaGenerator;
|
||||||
@@ -15,8 +16,8 @@ public class StandardWordClickImageCaptchaGeneratorTest {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||||
|
|
||||||
StandardRandomWordClickImageCaptchaGenerator defaultImageCaptchaResourceManager =
|
ImageCaptchaGenerator defaultImageCaptchaResourceManager =
|
||||||
new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, true);
|
new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, true).init();
|
||||||
|
|
||||||
GenerateParam generateParam = new GenerateParam();
|
GenerateParam generateParam = new GenerateParam();
|
||||||
generateParam.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
|
generateParam.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);
|
||||||
|
|||||||
Reference in New Issue
Block a user