package com.iplatform.base.captcha; import com.iplatform.base.PlatformRuntimeException; import com.iplatform.base.util.RandomUtils; import com.iplatform.base.util.VerifyImgUtil; import com.walker.infrastructure.utils.Base64Utils; import com.walker.web.CaptchaResult; import com.walker.web.CaptchaType; import com.walker.web.util.IdUtils; import javax.imageio.ImageIO; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.util.Objects; import java.util.Random; /** * 电商模块使用的拼图验证,提供者实现。 * @author 时克英 * @date 2023-06-27 */ public class BlockPuzzleCaptchaProvider extends AbstractCaptchaProvider{ private final static int IMAGE_BG_SIZE = 6; public BlockPuzzleCaptchaProvider(){ // String imageName = null; // try{ // BufferedImage image = null; // for(int i=1; i 0) { int position = 0; if (originalWidth - x - 5 > jigsawWidth * 2) { //在原扣图右边插入干扰图 position = RandomUtils.getRandomInt(x + jigsawWidth + 5, originalWidth - jigsawWidth); } else { //在原扣图左边插入干扰图 position = RandomUtils.getRandomInt(100, x - jigsawWidth - 5); } while (true) { // String s = ImageUtils.getslidingBlock(); String s = this.getSlidingBlock(); if (!jigsawImageBase64.equals(s)) { interferenceByTemplate(originalImage, Objects.requireNonNull(VerifyImgUtil.getBase64StrToImage(s)), position, 0); break; } } } if (captchaInterferenceOptions > 1) { while (true) { String s = this.getSlidingBlock(); if (!jigsawImageBase64.equals(s)) { Integer randomInt = RandomUtils.getRandomInt(jigsawWidth, 100 - jigsawWidth); interferenceByTemplate(originalImage, Objects.requireNonNull(VerifyImgUtil.getBase64StrToImage(s)), randomInt, 0); break; } } } // 设置“抗锯齿”的属性 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); graphics.drawImage(newJigsawImage, 0, 0, null); graphics.dispose(); ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。 ImageIO.write(newJigsawImage, IMAGE_TYPE_PNG, os);//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。 byte[] jigsawImages = os.toByteArray(); ByteArrayOutputStream oriImagesOs = new ByteArrayOutputStream();//新建流。 ImageIO.write(originalImage, IMAGE_TYPE_JPG, oriImagesOs);//利用ImageIO类提供的write方法,将bi以jpg图片的数据模式写入流。 byte[] oriCopyImages = oriImagesOs.toByteArray(); // Base64.Encoder encoder = Base64.getEncoder(); // dataVO.setOriginalImageBase64(encoder.encodeToString(oriCopyImages).replaceAll("\r|\n", "")); jigsawResult.setImageSourceBase64(Base64Utils.encode(oriCopyImages)); //point信息不传到前端,只做后端check校验 // dataVO.setPoint(point); // dataVO.setJigsawImageBase64(encoder.encodeToString(jigsawImages).replaceAll("\r|\n", "")); jigsawResult.setImageBlockBase64(Base64Utils.encode(jigsawImages)); // dataVO.setToken(RandomUtils.getUUID()); jigsawResult.setUuid(IdUtils.simpleUUID()); // dataVO.setSecretKey(point.getSecretKey()); // base64StrToImage(encoder.encodeToString(oriCopyImages), "D:\\原图.png"); // base64StrToImage(encoder.encodeToString(jigsawImages), "D:\\滑动.png"); //将坐标信息存入redis中 // String codeKey = String.format(REDIS_CAPTCHA_KEY, dataVO.getToken()); // CaptchaServiceFactory.getCache(cacheType).set(codeKey, JsonUtil.toJSONString(point), EXPIRESIN_SECONDS); logger.debug("token:{},point:{}", jigsawResult.getUuid(), jigsawResult.getX(), jigsawResult.getY()); return jigsawResult; } catch (Exception e) { logger.error("生成验证拼图错误:" + e.getMessage(), e); return null; } } private String getSlidingBlock() throws Exception{ // String[] strings = fileNameMap.get(CaptchaBaseMapEnum.SLIDING_BLOCK.getCodeValue()); // if (null == strings || strings.length == 0) { // return null; // } Integer randomInt = RandomUtils.getRandomInt(0, IMAGE_BG_SIZE); String blockName = "images/jigsaw/slidingBlock/" + randomInt + ".png"; // BufferedImage image = this.imageCacheMap.get(blockName); BufferedImage image = this.loadBufferedImage(blockName); if(image == null){ image = this.loadBufferedImage("images/jigsaw/slidingBlock/1.png"); } return VerifyImgUtil.getBase64(image); } /** * 随机生成拼图坐标 * * @param originalWidth * @param originalHeight * @param jigsawWidth * @param jigsawHeight * @return */ private static JigsawResult generateJigsawPoint(int originalWidth, int originalHeight, int jigsawWidth, int jigsawHeight) { Random random = new Random(); int widthDifference = originalWidth - jigsawWidth; int heightDifference = originalHeight - jigsawHeight; int x, y = 0; if (widthDifference <= 0) { x = 5; } else { x = random.nextInt(originalWidth - jigsawWidth - 100) + 100; } if (heightDifference <= 0) { y = 5; } else { y = random.nextInt(originalHeight - jigsawHeight) + 5; } // String key = null; // if (captchaAesStatus) { // key = AESUtil.getKey(); // } // return new PointVO(x, y, key); JigsawResult jigsawResult = new JigsawResult(); jigsawResult.setX(x); jigsawResult.setY(y); return jigsawResult; } /** * @param oriImage 原图 * @param templateImage 模板图 * @param newImage 新抠出的小图 * @param x 随机扣取坐标X * @param y 随机扣取坐标y * @throws Exception */ private static void cutByTemplate(BufferedImage oriImage, BufferedImage templateImage, BufferedImage newImage, int x, int y) { //临时数组遍历用于高斯模糊存周边像素值 int[][] martrix = new int[3][3]; int[] values = new int[9]; int xLength = templateImage.getWidth(); int yLength = templateImage.getHeight(); // 模板图像宽度 for (int i = 0; i < xLength; i++) { // 模板图片高度 for (int j = 0; j < yLength; j++) { // 如果模板图像当前像素点不是透明色 copy源文件信息到目标图片中 int rgb = templateImage.getRGB(i, j); if (rgb < 0) { newImage.setRGB(i, j, oriImage.getRGB(x + i, y + j)); //抠图区域高斯模糊 readPixel(oriImage, x + i, y + j, values); fillMatrix(martrix, values); oriImage.setRGB(x + i, y + j, avgMatrix(martrix)); } //防止数组越界判断 if (i == (xLength - 1) || j == (yLength - 1)) { continue; } int rightRgb = templateImage.getRGB(i + 1, j); int downRgb = templateImage.getRGB(i, j + 1); //描边处理,,取带像素和无像素的界点,判断该点是不是临界轮廓点,如果是设置该坐标像素是白色 if ((rgb >= 0 && rightRgb < 0) || (rgb < 0 && rightRgb >= 0) || (rgb >= 0 && downRgb < 0) || (rgb < 0 && downRgb >= 0)) { newImage.setRGB(i, j, Color.white.getRGB()); oriImage.setRGB(x + i, y + j, Color.white.getRGB()); } } } } private static void readPixel(BufferedImage img, int x, int y, int[] pixels) { int xStart = x - 1; int yStart = y - 1; int current = 0; for (int i = xStart; i < 3 + xStart; i++) { for (int j = yStart; j < 3 + yStart; j++) { int tx = i; if (tx < 0) { tx = -tx; } else if (tx >= img.getWidth()) { tx = x; } int ty = j; if (ty < 0) { ty = -ty; } else if (ty >= img.getHeight()) { ty = y; } pixels[current++] = img.getRGB(tx, ty); } } } private static void fillMatrix(int[][] matrix, int[] values) { int filled = 0; for (int i = 0; i < matrix.length; i++) { int[] x = matrix[i]; for (int j = 0; j < x.length; j++) { x[j] = values[filled++]; } } } private static int avgMatrix(int[][] matrix) { int r = 0; int g = 0; int b = 0; for (int i = 0; i < matrix.length; i++) { int[] x = matrix[i]; for (int j = 0; j < x.length; j++) { if (j == 1) { continue; } Color c = new Color(x[j]); r += c.getRed(); g += c.getGreen(); b += c.getBlue(); } } return new Color(r / 8, g / 8, b / 8).getRGB(); } /** * 干扰抠图处理 * * @param oriImage 原图 * @param templateImage 模板图 * @param x 随机扣取坐标X * @param y 随机扣取坐标y * @throws Exception */ private static void interferenceByTemplate(BufferedImage oriImage, BufferedImage templateImage, int x, int y) { //临时数组遍历用于高斯模糊存周边像素值 int[][] martrix = new int[3][3]; int[] values = new int[9]; int xLength = templateImage.getWidth(); int yLength = templateImage.getHeight(); // 模板图像宽度 for (int i = 0; i < xLength; i++) { // 模板图片高度 for (int j = 0; j < yLength; j++) { // 如果模板图像当前像素点不是透明色 copy源文件信息到目标图片中 int rgb = templateImage.getRGB(i, j); if (rgb < 0) { //抠图区域高斯模糊 readPixel(oriImage, x + i, y + j, values); fillMatrix(martrix, values); oriImage.setRGB(x + i, y + j, avgMatrix(martrix)); } //防止数组越界判断 if (i == (xLength - 1) || j == (yLength - 1)) { continue; } int rightRgb = templateImage.getRGB(i + 1, j); int downRgb = templateImage.getRGB(i, j + 1); //描边处理,,取带像素和无像素的界点,判断该点是不是临界轮廓点,如果是设置该坐标像素是白色 if ((rgb >= 0 && rightRgb < 0) || (rgb < 0 && rightRgb >= 0) || (rgb >= 0 && downRgb < 0) || (rgb < 0 && downRgb >= 0)) { oriImage.setRGB(x + i, y + j, Color.white.getRGB()); } } } } }