1. 程式人生 > >Java工具--滑塊驗證碼生成

Java工具--滑塊驗證碼生成

1概述

針對當前網際網路流行的滑塊驗證碼,這裡做了一個工具,適用於前後端分離的專案。工具將摳圖和摳圖後的原圖以位元組碼的形式輸出。

2工具原始碼

package com.liutao.util;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
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;

/**
 * 滑塊驗證工具類
 *
 * @author: LIUTAO
 * @Description:
 * @Date: Created in 10:57 2018/6/25
 * @Modified By:
 */
public class VerifyImageUtil {
    private static int ORI_WIDTH = 590;  //原始檔寬度
    private static int ORI_HEIGHT = 360;  //原始檔高度
    private static int X;  //摳圖座標x
    private static int Y;  //摳圖座標y
    private static int WIDTH;  //模板圖寬度
    private static int HEIGHT;  //模板圖高度
    private static float xPercent;  //X位置移動百分比
    private static float yPercent;  //Y位置移動百分比


    public static int getX() {
        return X;
    }

    public static int getY() {
        return Y;
    }

    public static float getxPercent() {
        return xPercent;
    }

    public static float getyPercent() {
        return yPercent;
    }

    /**
     * 根據模板切圖
     * newImage:摳圖直位元組碼
     * oriCopyImage:原圖位元組碼
     *
     * @param templateFile
     * @param targetFile
     * @param templateType
     * @param targetType
     * @return
     * @throws Exception
     */
    public static Map<String, byte[]> pictureTemplatesCut(File templateFile, File targetFile, String templateType, String targetType) 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 type is empty");
        }
        // 原始檔流
        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);
                } 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++) {
                // 如果模板影象當前畫素點不是白色 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;
    }

    /**
     * 隨機生成摳圖座標
     */
    private static void generateCutoutCoordinates() {
        Random random = new Random();
        int widthDifference = ORI_WIDTH - WIDTH;
        int heightDifference = ORI_HEIGHT - HEIGHT;

        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);

        xPercent = Float.parseFloat(numberFormat.format((float) X / (float) ORI_WIDTH));
        yPercent = Float.parseFloat(numberFormat.format((float) Y / (float) ORI_HEIGHT));
    }
}


3測試程式碼

public void testImg() throws Exception {
        Map<String, byte[]> pictureMap;
        File templateFile;  //模板圖片
        File targetFile;  //
        Random random = new Random();
        int templateNo = random.nextInt(4) + 1;
        int targetNo = random.nextInt(20) + 1;

        InputStream stream = getClass().getClassLoader().getResourceAsStream("static/templates/" + templateNo + ".png");
        templateFile = new File(templateNo + ".png");
        FileUtils.copyInputStreamToFile(stream, templateFile);

        stream = getClass().getClassLoader().getResourceAsStream("static/targets/" + targetNo + ".jpg");
        targetFile = new File(targetNo + ".jpg");
        FileUtils.copyInputStreamToFile(stream, targetFile);
        pictureMap = VerifyImageUtil.pictureTemplatesCut(templateFile, targetFile, "png", "jpg");
        byte[] oriCopyImages = pictureMap.get("oriCopyImage");
        byte[] newImages = pictureMap.get("newImage");

        FileOutputStream fout = new FileOutputStream("C:/Users/Administrator/Desktop/oriCopyImage.png");
        //將位元組寫入檔案
        try {
            fout.write(oriCopyImages);
        } catch (IOException e) {
            e.printStackTrace();
        }
        fout.close();

        FileOutputStream newImageFout = new FileOutputStream("C:/Users/Administrator/Desktop/newImage.png");
        //將位元組寫入檔案
        newImageFout.write(newImages);
        newImageFout.close();
    }

以上就是滑塊驗證碼的使用示例。具體的圖片資源和原始碼請參考:滑塊驗證碼。

有疑問請在下方評論區留言。歡迎交流!