生成图形验证码工具类

生成图形验证码工具类


import com.alibaba.fastjson.JSONObject;
import com.common.util.FileUtils;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;


/**
 * 图形验证码生成工具类
 */
@Slf4j
@UtilityClass
public class VerifyImageUtil {
    private static int ORI_WIDTH = 280;  //源文件宽度
    private static int ORI_HEIGHT = 171;  //源文件高度

    private static int WIDTH;  //模板图宽度
    private static int HEIGHT;  //模板图高度

    private static int BOLD = 5;
    /**
     * 源文件宽度
     */
    private static int ORIGINAL_WIDTH = 240;
    /**
     * 源文件高度
     */
    private static int ORIGINAL_HEIGHT = 160;

    private static final String IMG_FILE_TYPE = "png";

    public static Map getImgVerify(HttpServletRequest request) throws Exception {
        log.info("进入getImgVerify方法");
        long startTime = System.currentTimeMillis();
        long startTime1 = System.currentTimeMillis();

        HashMap<String, Object> resultMap = new HashMap<>();
        Random random = new Random();

        //模板图片
        InputStream stream = VerifyImageUtil.class.getClassLoader().getResourceAsStream("templates/template.png");
        File templateFile = new File("template.png");
        FileUtils.copyInputStreamToFile(stream, templateFile);
        log.info("生成图形验证码 获取templates,耗时:{} ===== end =====", System.currentTimeMillis() - startTime1);
        startTime1 = System.currentTimeMillis();

        int targetNo = random.nextInt(10) + 1;
        stream = VerifyImageUtil.class.getClassLoader().getResourceAsStream("targets/" + targetNo + ".png");
        File targetFile = new File(targetNo + ".png");
        FileUtils.copyInputStreamToFile(stream, targetFile);
        log.info("生成图形验证码 获取targets:{}.jpg,耗时:{} ===== end =====", targetNo, System.currentTimeMillis() - startTime1);
        startTime1 = System.currentTimeMillis();

        Map<String, Object> pictureMap = pictureTemplateCut(templateFile, targetFile);

        /**
         * 将用户手机号以及滑块坐标保存入缓存
         */
        HashMap sessionMap = new HashMap();
        sessionMap.put("X", pictureMap.get("X"));
        sessionMap.put("Y", pictureMap.get("Y"));

        log.info("X:" + sessionMap.get("X"));
        log.info("Y:" + sessionMap.get("Y"));
        request.getSession().setAttribute("imgXyMap", sessionMap);

        log.info("生成图形验证码 获取pictureMap,耗时:{} ===== end =====", System.currentTimeMillis() - startTime1);
        log.info("生成图形验证码 从request获取imgXyMap:{}", JSONObject.toJSONString(request.getSession().getAttribute("imgXyMap")));

        resultMap.put("newImage", pictureMap.get("newImage"));
        resultMap.put("oriCopyImage", pictureMap.get("oriCopyImage"));
        resultMap.put("Y", pictureMap.get("Y"));
        resultMap.put("oirWidth", pictureMap.get("oirWidth"));
        resultMap.put("oriHeight", pictureMap.get("oriHeight"));
        resultMap.put("newWidth", pictureMap.get("newWidth"));
        resultMap.put("newHeight", pictureMap.get("newHeight"));

        log.info("生成图形验证码 oirWidth:{},oriHeight:{},newWidth:{},newHeight:{}",
                pictureMap.get("oirWidth"), pictureMap.get("oriHeight"), pictureMap.get("newWidth"), pictureMap.get("newHeight"));
        log.info("生成图形验证码,总耗时:{} ===== end =====", System.currentTimeMillis() - startTime);

        return resultMap;
    }

    /**
     * 根据模版切图
     * @param templateFile  模版文件
     * @param targetFile 目标文件
     * @param templateType  模版文件类型
     * @param targetType    目标文件类型
     * @param X X坐标
     * @param Y Y坐标
     * @return
     * @throws Exception
     */
    private static Map<String, byte[]> pictureTemplatesCut(File templateFile, File targetFile, String templateType, String targetType, int X, int Y) throws Exception {
        Map<String, byte[]> pictureMap = new HashMap<>();
        // 文件类型
        String templateFiletype = templateType;
        String oriFiletype = targetType;
        if (StringUtils.isEmpty(templateFiletype) || StringUtils.isEmpty(oriFiletype)) {
            throw new RuntimeException("文件类型为空");
        }
        // 源文件流
        File Orifile = targetFile;
        InputStream oriis = new FileInputStream(Orifile);

        // 模板图
        BufferedImage imageTemplate = ImageIO.read(templateFile);
        WIDTH = imageTemplate.getWidth();
        HEIGHT = imageTemplate.getHeight();
        //generateCutoutCoordinates();
        // 最终图像
        BufferedImage newImage = new BufferedImage(WIDTH, HEIGHT, imageTemplate.getType());
        Graphics2D graphics = newImage.createGraphics();
        graphics.setBackground(Color.white);

        int bold = 5;
        // 获取感兴趣的目标区域
        BufferedImage targetImageNoDeal = getTargetArea(X, Y, WIDTH, HEIGHT, oriis, oriFiletype);

        // 根据模板图片抠图
        newImage = DealCutPictureByTemplate(targetImageNoDeal, imageTemplate, newImage);

        // 设置“抗锯齿”的属性
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        graphics.drawImage(newImage, 0, 0, null);
        graphics.dispose();

        ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        ImageIO.write(newImage, "png", os);//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
        byte[] newImages = os.toByteArray();
        pictureMap.put("newImage", newImages);

        // 源图生成遮罩
        BufferedImage oriImage = ImageIO.read(Orifile);
        byte[] oriCopyImages = DealOriPictureByTemplate(oriImage, imageTemplate, X, Y);
        pictureMap.put("oriCopyImage", oriCopyImages);
        return pictureMap;
    }

    /**
     * 抠图后原图生成
     *
     * @param oriImage
     * @param templateImage
     * @param x
     * @param y
     * @return
     * @throws Exception
     */
    private static byte[] DealOriPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage, int x,
                                                   int y) throws Exception {

        // 源文件备份图像矩阵 支持alpha通道的rgb图像
        BufferedImage ori_copy_image = new BufferedImage(oriImage.getWidth(), oriImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);

        //copy 源图做不透明处理
        for (int i = 0; i < oriImageData.length; i++) {
            for (int j = 0; j < oriImageData[0].length; j++) {
                int rgb = oriImage.getRGB(i, j);
                int r = (0xff & rgb);
                int g = (0xff & (rgb >> 8));
                int b = (0xff & (rgb >> 16));
                //无透明处理
                rgb = r + (g << 8) + (b << 16) + (255 << 24);
                ori_copy_image.setRGB(i, j, rgb);
            }
        }

        for (int i = 0; i < templateImageData.length; i++) {
            for (int j = 0; j < templateImageData[0].length - 5; j++) {
                int rgb = templateImage.getRGB(i, j);
                //对源文件备份图像(x+i,y+j)坐标点进行透明处理
                if (rgb != 16777215 && rgb <= 0) {
                    int rgb_ori = ori_copy_image.getRGB(x + i, y + j);
                    int r = (0xff & rgb_ori);
                    int g = (0xff & (rgb_ori >> 8));
                    int b = (0xff & (rgb_ori >> 16));
                    rgb_ori = r + (g << 8) + (b << 16) + (150 << 24);


                    //源代码
                    //ori_copy_image.setRGB(x + i, y + j, rgb_ori);

                    //修改后的值
                    ori_copy_image.setRGB(x + i, y + j, 65280);
                } else {
                    //do nothing
                }
            }
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        ImageIO.write(ori_copy_image, "png", os);//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
        byte b[] = os.toByteArray();//从流中获取数据数组。
        return b;
    }


    /**
     * 根据模板图片抠图
     *
     * @param oriImage
     * @param templateImage
     * @return
     */

    private static BufferedImage DealCutPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage,
                                                          BufferedImage targetImage) throws Exception {
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);
        // 模板图像宽度
        for (int i = 0; i < templateImageData.length; i++) {
            // 模板图片高度
            for (int j = 0; j < templateImageData[0].length; j++) {
                //防止数组越界判断
                if (i == (templateImageData.length - 1) || j == (templateImageData[0].length - 1)) {
                    continue;
                }
                // 如果模板图像当前像素点不是白色 copy源文件信息到目标图片中
                int rgb = templateImageData[i][j];
                if (rgb != 16777215 && rgb <= 0) {
                    targetImage.setRGB(i, j, oriImageData[i][j]);
                }
            }
        }
        return targetImage;
    }

    /**
     * 获取目标区域
     *
     * @param x            随机切图坐标x轴位置
     * @param y            随机切图坐标y轴位置
     * @param targetWidth  切图后目标宽度
     * @param targetHeight 切图后目标高度
     * @param ois          源文件输入流
     * @return
     * @throws Exception
     */
    private static BufferedImage getTargetArea(int x, int y, int targetWidth, int targetHeight, InputStream ois,
                                               String filetype) throws Exception {
        Iterator<ImageReader> imageReaderList = ImageIO.getImageReadersByFormatName(filetype);
        ImageReader imageReader = imageReaderList.next();
        // 获取图片流
        ImageInputStream iis = ImageIO.createImageInputStream(ois);
        // 输入源中的图像将只按顺序读取
        imageReader.setInput(iis, true);

        ImageReadParam param = imageReader.getDefaultReadParam();
        Rectangle rec = new Rectangle(x, y, targetWidth, targetHeight);
        param.setSourceRegion(rec);
        BufferedImage targetImage = imageReader.read(0, param);
        return targetImage;
    }

    /**
     * 生成图像矩阵
     *
     * @param
     * @return
     * @throws Exception
     */
    private static int[][] getData(BufferedImage bimg) throws Exception {
        int[][] data = new int[bimg.getWidth()][bimg.getHeight()];
        for (int i = 0; i < bimg.getWidth(); i++) {
            for (int j = 0; j < bimg.getHeight(); j++) {
                data[i][j] = bimg.getRGB(i, j);
            }
        }
        return data;
    }

    /**
     * @description 随机生成抠图坐标
     * @author LL
     */
    private static HashMap generateCutoutCoordinates() {
        Random random = new Random();
        int widthDifference = ORI_WIDTH - WIDTH;
        int heightDifference = ORI_HEIGHT - HEIGHT;

        int X, Y;
        if (widthDifference <= 0) {
            X = 5;

        } else {
            X = random.nextInt(ORI_WIDTH - WIDTH) + 5;
        }

        if (heightDifference <= 0) {
            Y = 5;
        } else {
            Y = random.nextInt(ORI_HEIGHT - HEIGHT) + 5;
        }
        NumberFormat numberFormat = NumberFormat.getInstance();
        numberFormat.setMaximumFractionDigits(2);

        HashMap map = new HashMap();
        map.put("X", X);
        map.put("Y", Y);
        return map;

    }

    public static Boolean checkImgVerify(String X, HashMap imgXyMap) {
        //Y轴固定,只需校验X轴
        if (StringUtils.isEmpty(X) || !StringUtils.isNumeric(X)) {
            return false;
        }
        if (imgXyMap != null && !imgXyMap.isEmpty() && imgXyMap.get("X") != null) {
            Integer reqX = Integer.parseInt(imgXyMap.get("X").toString());
            Integer getX = Integer.parseInt(X);
            Integer comX = reqX - getX;
            //允许误差±3(根据自身业务逻辑调整)
            if (-3 < comX && comX < 3) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 根据模板切图
     *
     * @param templateFile
     * @param targetFile
     * @return
     */
    private static Map<String, Object> pictureTemplateCut(File templateFile, File targetFile) throws Exception {
        log.info("pictureTemplateCut --------------------- 开始");
        Map<String, Object> pictureMap = new HashMap<>();

        //模板图
        BufferedImage templateImage = ImageIO.read(templateFile);
        //模板图宽度
        int templateWidth = templateImage.getWidth();
        //模板图高度
        int templateHeight = templateImage.getHeight();

        //原图
        BufferedImage originalImage = ImageIO.read(targetFile);
        //原图宽度
        int oriImageWidth = originalImage.getWidth();
        //原图高度
        int oriImageHeight = originalImage.getHeight();

        /**
         *  随机生成抠图坐标X,Y
         *  X轴距离右端targetWidth  Y轴距离底部targetHeight以上
         */
        Random random = new Random();
        log.info("oriImageHeight:{},templateHeight:{}", oriImageHeight, templateHeight);
        int widthRandom = random.nextInt(oriImageWidth - 2 * templateWidth) + templateWidth;
        int heightRandom = random.nextInt(oriImageHeight - templateHeight);
        log.info("原图大小X={},Y={},随机生成的坐标 X={},Y={}", oriImageWidth, oriImageHeight, widthRandom, heightRandom);

        /**
         * 新建一个和模板一样大小的图像
         * TYPE_4BYTE_ABGR表示具有8位RGBA颜色分量的图像
         * 取imageTemplate.getType()
         */

        BufferedImage newImage = new BufferedImage(templateWidth, templateHeight, templateImage.getType());
        //得到画笔对象
        Graphics2D graphics = newImage.createGraphics();
        //如果需要生成RGB格式,需要做如下配置,Transparency 设置透明
        newImage = graphics.getDeviceConfiguration().createCompatibleImage(templateWidth, templateHeight, Transparency.TRANSLUCENT);

        /**
         * 新建的图像根据模板颜色赋值,源图生成遮罩
         */
        cutByTemplate(originalImage, templateImage, newImage, widthRandom, heightRandom);

        /**
         * 设置“抗锯齿”的属性
         */
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setStroke(new BasicStroke(BOLD, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        graphics.drawImage(newImage, 0, 0, null);
        graphics.dispose();

        //新建流
        ByteArrayOutputStream newImageOs = new ByteArrayOutputStream();
        //利用ImageIO类提供的write方法,将png图片的数据模式写入流。
        ImageIO.write(newImage, IMG_FILE_TYPE, newImageOs);
        byte[] newImagebyte = newImageOs.toByteArray();

        ByteArrayOutputStream oriImagesOs = new ByteArrayOutputStream();
        ImageIO.write(originalImage, IMG_FILE_TYPE, oriImagesOs);
        byte[] oriImageByte = oriImagesOs.toByteArray();

        //原图片的抠图(小图)
        pictureMap.put("newImage", Base64Utils.encode(newImagebyte));
        //原图片(大图)
        pictureMap.put("oriCopyImage", Base64Utils.encode(oriImageByte));
        //抠图坐标 X
        pictureMap.put("X", widthRandom);
        //抠图坐标 Y
        pictureMap.put("Y", heightRandom);
        //原图(大图)宽度
        pictureMap.put("oirWidth", ORIGINAL_WIDTH);
        //原图(大图)高度
        pictureMap.put("oriHeight", ORIGINAL_HEIGHT);
        //抠图(小图)宽度
        pictureMap.put("newWidth", templateWidth);
        //抠图(小图)高度
        pictureMap.put("newHeight", templateHeight);
        return pictureMap;
    }

    /**
     * @param originalImage 原图
     * @param templateImage 模板图
     * @param newImage      新扣除的小图
     * @param x             随机扣取的X坐标
     * @param y             随机扣取的Y坐标
     */
    private static void cutByTemplate(BufferedImage originalImage, 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, originalImage.getRGB(x + i, y + j));
                    //抠图区域高斯模糊
                    readPixel(originalImage, x + i, y + j, values);
                    fillMatrix(martrix, values);
//                    originalImage.setRGB(x + i, y + j, avgMatrix(martrix));
                    originalImage.setRGB(x + i, y + j, Color.white.getRGB());
                }
                //防止数组越界判断
                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.lightGray.getRGB());
                    originalImage.setRGB(x + i, y + j, Color.lightGray.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();
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中生成图形验证码的工具类很多,其中比较常用的是使用第三方库生成验证码图片,比如Google的kaptcha和阿里巴巴的GifCaptcha等。 以下是一个使用kaptcha生成图形验证码的示例代码: ```java import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.util.Properties; public class CaptchaUtils { private static DefaultKaptcha captchaProducer; static { captchaProducer = new DefaultKaptcha(); Properties properties = new Properties(); // 设置验证码图片的宽度 properties.setProperty("kaptcha.image.width", "120"); // 设置验证码图片的高度 properties.setProperty("kaptcha.image.height", "40"); // 设置验证码字符的字体 properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier"); // 设置验证码字符个数 properties.setProperty("kaptcha.textproducer.char.length", "4"); Config config = new Config(properties); captchaProducer.setConfig(config); } public static BufferedImage generateCaptcha(String text) { return captchaProducer.createImage(text); } public static byte[] generateCaptchaBytes(String text) { try { BufferedImage image = captchaProducer.createImage(text); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "png", outputStream); return outputStream.toByteArray(); } catch (Exception e) { e.printStackTrace(); return null; } } } ``` 使用示例: ```java // 生成验证码图片 BufferedImage image = CaptchaUtils.generateCaptcha("abcd"); // 将验证码图片转换成字节数组 byte[] bytes = CaptchaUtils.generateCaptchaBytes("abcd"); ``` 这个工具类使用了kaptcha库,可以方便地生成图形验证码图片。可以通过修改配置文件来定制验证码图片的样式和字符个数等参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值