diff --git a/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java b/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java index 13800c6..2e5208c 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java @@ -306,4 +306,15 @@ public class CaptchaImageUtils { } 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))); + } + } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/common/util/FontUtils.java b/src/main/java/cloud/tianai/captcha/template/slider/common/util/FontUtils.java new file mode 100644 index 0000000..f41c141 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/common/util/FontUtils.java @@ -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))); + } + + + + +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java index 26ee06a..4c91ade 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/AbstractImageCaptchaGenerator.java @@ -56,7 +56,8 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener * @param formatType 格式化类型 * @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(); ImageIO.write(bufferedImage, formatType, byteArrayOutputStream); //转换成字节码 diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java index 6ca3fbb..92ab3e9 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java @@ -13,4 +13,6 @@ public interface CaptchaTypeConstant { String ROTATE = "ROTATE"; /** 拼接.*/ String CONCAT = "CONCAT"; + /** 文字点选.*/ + String WORD_CLICK = "WORD_CLICK"; } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java index 71711f6..1695a9c 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/ImageCaptchaInfo.java @@ -30,8 +30,12 @@ public class ImageCaptchaInfo { private Integer sliderImageWidth; /** 滑块图片高度. */ private Integer sliderImageHeight; - /** 随机值.*/ + /** 随机值. */ private Integer randomX; + /** 容错值, 可以为空 默认 0.02容错,校验的时候用. */ + private Float tolerant; + /** 验证码类型.*/ + private String type; /** * 扩展字段 */ @@ -43,7 +47,8 @@ public class ImageCaptchaInfo { Integer bgImageHeight, Integer sliderImageWidth, Integer sliderImageHeight, - Integer randomX) { + Integer randomX, + String type) { this.backgroundImage = backgroundImage; this.sliderImage = sliderImage; this.bgImageWidth = bgImageWidth; @@ -51,6 +56,7 @@ public class ImageCaptchaInfo { this.sliderImageWidth = sliderImageWidth; this.sliderImageHeight = sliderImageHeight; this.randomX = randomX; + this.type = type; } public static ImageCaptchaInfo of(String backgroundImage, @@ -59,8 +65,9 @@ public class ImageCaptchaInfo { Integer bgImageHeight, Integer sliderImageWidth, Integer sliderImageHeight, - Integer randomKey) { - return new ImageCaptchaInfo( backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey); + Integer randomKey, + String type) { + return new ImageCaptchaInfo(backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, randomKey, type); } } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java index a2ef472..d96fe2f 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/RotateImageCaptchaInfo.java @@ -1,5 +1,6 @@ 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.EqualsAndHashCode; import lombok.ToString; @@ -17,8 +18,8 @@ public class RotateImageCaptchaInfo extends ImageCaptchaInfo { * 旋转多少度 */ private Double degree; - - + /** 旋转图片的容错值大一点. */ + public static final Float DEFAULT_TOLERANT = 0.05F; public static RotateImageCaptchaInfo of(Double degree, Integer randomX, @@ -32,11 +33,14 @@ public class RotateImageCaptchaInfo extends ImageCaptchaInfo { rotateImageCaptchaInfo.setDegree(degree); rotateImageCaptchaInfo.setRandomX(randomX); rotateImageCaptchaInfo.setBackgroundImage(backgroundImage); + rotateImageCaptchaInfo.setTolerant(DEFAULT_TOLERANT); rotateImageCaptchaInfo.setSliderImage(sliderImage); rotateImageCaptchaInfo.setBgImageWidth(bgImageWidth); rotateImageCaptchaInfo.setBgImageHeight(bgImageHeight); rotateImageCaptchaInfo.setSliderImageWidth(sliderImageWidth); rotateImageCaptchaInfo.setSliderImageHeight(sliderImageHeight); + // 类型为旋转图片验证码 + rotateImageCaptchaInfo.setType(CaptchaTypeConstant.ROTATE); return rotateImageCaptchaInfo; } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java index 563cffd..24d806d 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/SliderImageCaptchaInfo.java @@ -1,5 +1,6 @@ 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.EqualsAndHashCode; import lombok.ToString; @@ -36,7 +37,7 @@ public class SliderImageCaptchaInfo extends ImageCaptchaInfo { sliderImageCaptchaInfo.setBgImageHeight(bgImageHeight); sliderImageCaptchaInfo.setSliderImageWidth(sliderImageWidth); sliderImageCaptchaInfo.setSliderImageHeight(sliderImageHeight); - + sliderImageCaptchaInfo.setType(CaptchaTypeConstant.SLIDER); return sliderImageCaptchaInfo; } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/WordClickImageCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/WordClickImageCaptchaInfo.java new file mode 100644 index 0000000..5d48fd2 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/model/dto/WordClickImageCaptchaInfo.java @@ -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 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; + + } +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardClickImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardClickImageCaptchaGenerator.java new file mode 100644 index 0000000..a00a866 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardClickImageCaptchaGenerator.java @@ -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 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 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 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 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")); + } + } +} diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java index da1ea4a..a88f65d 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java @@ -27,7 +27,7 @@ import static cloud.tianai.captcha.template.slider.generator.impl.StandardSlider */ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGenerator { - protected final ImageCaptchaResourceManager imageCaptchaResourceManager; + protected ImageCaptchaResourceManager imageCaptchaResourceManager; public StandardConcatImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) { this.imageCaptchaResourceManager = imageCaptchaResourceManager; @@ -79,7 +79,16 @@ public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGen private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, BufferedImage bgImage, BufferedImage sliderImage, GenerateParam param) { String backGroundImageBase64 = transform(bgImage, param.getBackgroundFormatName()); 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 diff --git a/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleImageCaptchaValidator.java b/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleImageCaptchaValidator.java index 1e9935e..cd9383f 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleImageCaptchaValidator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/validator/impl/SimpleImageCaptchaValidator.java @@ -1,6 +1,8 @@ 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.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.validator.ImageCaptchaValidator; import cloud.tianai.captcha.template.slider.validator.common.model.dto.SliderCaptchaTrack; @@ -26,6 +28,10 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator { @Setter 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() { } @@ -59,16 +65,28 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator { public Map generateSliderCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo) { Map map = new HashMap<>(8); 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; } @Override public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Map sliderCaptchaValidData) { - Float oriPercentage = getPercentage(sliderCaptchaTrack, sliderCaptchaValidData); - if (oriPercentage == null) { - // 没读取到百分比 - return false; - } + Float oriPercentage = getFloatParam(PERCENTAGE_KEY, sliderCaptchaValidData); + // 读容错值 + Float tolerantData = getFloatParam(TOLERANT_KEY, sliderCaptchaValidData, defaultTolerant); + // 读验证码类型 + String type = getStringParam(TYPE_KEY, sliderCaptchaValidData, CaptchaTypeConstant.SLIDER); + Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth(); if (bgImageWidth == null || bgImageWidth < 1) { // 没有背景图片宽度 @@ -79,35 +97,74 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator { // 没有滑动轨迹 return false; } - // 取最后一个滑动轨迹 - SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1); - // 计算百分比 - float calcPercentage = calcPercentage(lastTrack.getX(), bgImageWidth); - // 校验百分比 - return checkPercentage(calcPercentage, oriPercentage); + return doValid(sliderCaptchaTrack, sliderCaptchaValidData, oriPercentage, tolerantData, type); } - protected Float getPercentage(SliderCaptchaTrack sliderCaptchaTrack, Map sliderCaptchaValidData) { - Object percentage = sliderCaptchaValidData.get("percentage"); - if (percentage != null) { - if (percentage instanceof Number) { - return ((Number) percentage).floatValue(); + public boolean doValid(SliderCaptchaTrack sliderCaptchaTrack, + Map sliderCaptchaValidData, + Float oriPercentage, + Float tolerant, + String type) { + if (CaptchaTypeConstant.SLIDER.equals(type) + || CaptchaTypeConstant.ROTATE.equals(type) + || CaptchaTypeConstant.CONCAT.equals(type)) { + if (oriPercentage == null) { + // 没读取到百分比 + return false; + } + List 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 sliderCaptchaValidData) { + return getFloatParam(key, sliderCaptchaValidData, null); + } + + protected Float getFloatParam(String key, Map sliderCaptchaValidData, Float defaultData) { + Object data = sliderCaptchaValidData.get(key); + if (data != null) { + if (data instanceof Number) { + return ((Number) data).floatValue(); } try { - if (percentage instanceof String) { - return Float.parseFloat((String) percentage); + if (data instanceof String) { + return Float.parseFloat((String) data); } } catch (NumberFormatException e) { - log.error("从 sliderCaptchaValidData 读取到的 percentage无法转换成float类型, [{}]", percentage); + log.error("从 sliderCaptchaValidData 读取到的 " + key + "无法转换成float类型, [{}]", data); throw e; } } - log.warn("无法从 sliderCaptchaValidData 获取到 percentage"); - return null; + return defaultData; + } + + protected String getStringParam(String key, Map 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 sliderCaptchaValidData) { float percentage = calcPercentage(imageCaptchaInfo.getRandomX(), imageCaptchaInfo.getBgImageWidth()); - sliderCaptchaValidData.put("percentage", percentage); + sliderCaptchaValidData.put(PERCENTAGE_KEY, percentage); } } diff --git a/src/main/resources/META-INF/fonts/SIMSUN.TTC b/src/main/resources/META-INF/fonts/SIMSUN.TTC new file mode 100644 index 0000000..40e9693 Binary files /dev/null and b/src/main/resources/META-INF/fonts/SIMSUN.TTC differ diff --git a/src/main/test/java/example/StandardWordClickImageCaptchaGeneratorTest.java b/src/main/test/java/example/StandardWordClickImageCaptchaGeneratorTest.java new file mode 100644 index 0000000..4ef48f8 --- /dev/null +++ b/src/main/test/java/example/StandardWordClickImageCaptchaGeneratorTest.java @@ -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); + + } +}