圖片滑動驗證的實現


有時候在頁面登錄或者注冊的時候,為了防止不是機器人操作,會讓用戶手動來滑動圖片驗證。我在做項目時用到了這個功能,這里記錄一下自己的想法和做法。

實現的效果如圖所示:

好了,現在來說說想法。

關於圖片滑動驗證一般是要前后端來交互的。首先是要后台處理好圖片,然后將處理出來的圖片返回到前台。后台隨機抽取一張模板圖片,也就是完整的圖片,然后通過代碼來操作將一塊區域的圖扣出來,將扣掉的地方填成灰色或者黑色,這樣,素材就有了。

在返回到前台時,要將圖片轉碼為base編碼。在前台接收時,要使用如下方式來講base64編碼的圖片顯示:

$("#validateImage").attr("src", "data:image/png;base64,"+data.srcImage);
$("#slideImage").attr("src", "data:image/png;base64,"+data.cutImage);

然后前台通過滑動圖片來驗證。這種情況要存儲滑動的距離,也就是像素點。當然,圖片生成時也要將摳圖的坐標記錄,要和滑動后傳過來的數據進行驗證。

圖片處理類如下:我這個圖片處理類的半圓型的半徑並沒有算進那個坐標區域,所以在判斷是否拼圖正確的情況下必須要將那個半徑加上或者減去(專屬我自己的項目,符合自己的方式,如果不符合你的,請將想法吃透,然后在我的工具類中提煉出一些關鍵的技術,自己嘗試寫吧)。

package news.utils;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.imageio.ImageIO;

import news.bean.VerifyImage;
import sun.misc.BASE64Encoder;

/**
 * 處理圖片的工具類,用於實現前端的滑動驗證
 * @author 徐金仁
 */
public class VerifyImageUtils3 {
    //原圖片的寬度
    private static int ORI_HEIGHT = 160; 
    //原圖片的高度
    private static int ORI_WIDTH = 322; 
    //摳圖上面的半徑
    private static int RADIUS = 8;
    //摳圖區域的高度
    private static int TAM_HEIGHT = 40;
    //摳圖區域的寬度
    private static int TAM_WIDTH = 40;
    //摳圖內部矩形填充的大小
    private static int RAN_SIZE = 8;
    //摳圖的邊框寬度
    private static int BORDER_SIZE = 1;
    //
    private static int XPosition ;
    private static int YPosition ;
    
    
    Random random =new Random();
    
    
    /**
     * 提供的對外的公共接口,用於獲取處理后的圖片和摳圖的位置
     * @param filePath 存儲原始圖片的位置
     * @throws Exception 
     */
    public VerifyImage getImages(String filePath) throws Exception{
        VerifyImage ve = new VerifyImage();
        //隨機獲取一張圖片
        File imageFile = getRandomImage(filePath);
        BufferedImage oriImage = ImageIO.read(imageFile);
        ORI_HEIGHT = oriImage.getHeight();
        ORI_WIDTH = oriImage.getWidth();
        //獲取摳圖的坐標,防止不恰當的坐標會出現在圖片的邊沿,
        XPosition = TAM_WIDTH + random.nextInt(oriImage.getWidth() - TAM_WIDTH * 3);
        YPosition = TAM_HEIGHT + random.nextInt(oriImage.getHeight() - TAM_HEIGHT * 2);
        //獲取摳圖的區域,將這個圖片的坐標傳進去,來獲取到摳圖的位置和大小,就是將摳圖給定死
        int[][] blockData = getBlockData(oriImage);
        //處理圖片,將得到的兩張圖片放入map中
        Map<String, BufferedImage> dataMap = createVerifyImage(oriImage,blockData);
        //將那些圖片轉為base64存儲
        String srcImage = ImageBase64(dataMap.get("oriImage"));
        String cutImage = ImageBase64(dataMap.get("cutImage"));
        ve.setCutImage(cutImage);
        ve.setSrcImage(srcImage);
        ve.setXPosition(XPosition);
        ve.setYPosition(YPosition);
        return ve;
    }
    /**
     * 將圖片轉為base64存儲
     * @param bufferedImage 要轉化的圖片
     * @return
     * @throws IOException 
     */
    private String ImageBase64(BufferedImage bufferedImage) throws IOException {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         ImageIO.write(bufferedImage,"png",out);
            //轉成byte數組
            byte[] bytes = out.toByteArray();
            BASE64Encoder encoder = new BASE64Encoder();
            //生成BASE64編碼
            return encoder.encode(bytes);
    }
    /**
     * 處理圖片,將得到的兩張圖片放入map中
     * @param oriImage 原圖
     * @param blockData 整張布局的區域
     * @return 存儲圖片的map
     */
    private Map<String, BufferedImage> createVerifyImage(BufferedImage oriImage, int[][] blockData) {
        Map<String, BufferedImage> map = new HashMap<String, BufferedImage>();
        int h = blockData.length;
        int w = blockData[0].length;
        BufferedImage cutImage = new BufferedImage(TAM_WIDTH + 2*RADIUS,TAM_HEIGHT + 2*RADIUS,BufferedImage.TYPE_4BYTE_ABGR);
        int i2,j2;
        for(int i = 0; i < TAM_WIDTH + 2*RADIUS; i ++){
            for(int j = 0; j < TAM_HEIGHT + 2*RADIUS; j ++){
                i2 = i + XPosition - RADIUS;
                j2 = j + YPosition - RADIUS;
                if(blockData[i2][j2] == 2){
                    cutImage.setRGB(i, j, oriImage.getRGB(i2, j2));
                    oriImage.setRGB(i2, j2, Color.gray.getRGB());
                }
            }
        }
        map.put("cutImage", cutImage);
        map.put("oriImage", oriImage);
        return map;
    }
    /**
     * 獲取摳圖區域,返回一個數組,上面展現了摳圖的位置和具體區域
     * @param oriImage
     * @return
     */
    private int[][] getBlockData(BufferedImage oriImage) {
        ORI_HEIGHT = oriImage.getHeight();
        ORI_WIDTH = oriImage.getWidth();
        //要先選擇四邊中的一條邊來選擇圓心,1、 2、 3、 4分別代表上下左右
        int chooseR = random.nextInt(4) + 1;
        int[][] blockData = new int[ORI_WIDTH][ORI_HEIGHT];
        //矩形區域處理
        for(int i = 0; i <ORI_WIDTH; i++){
            for(int j = 0; j < ORI_HEIGHT; j ++){
                blockData[i][j] = 0;
                if(i >= XPosition && j>= YPosition && j<= YPosition + TAM_HEIGHT  && i<= XPosition + TAM_WIDTH ){
                    blockData[i][j] = 2;
                }
            }
        }
        //圓型區域凸出塊
        //(x - a)^2 + (y - b)^2 = r^2
        int x ;
        int y ;
        //圓形區域的凹陷塊
        int x2;
        int y2;
        String sx;
        if(chooseR== 1){//
            x = (XPosition + TAM_HEIGHT / 2);
            y = YPosition;
            sx = "下";
            x2 = (XPosition);
            y2 = YPosition + TAM_HEIGHT / 2;
        }else if(chooseR == 2){//
            x = (XPosition + TAM_HEIGHT / 2);
            y = YPosition + TAM_WIDTH ;
            sx = "上";
            x2 = (XPosition + TAM_HEIGHT );
            y2 = YPosition + TAM_HEIGHT / 2;
        }else if(chooseR == 3){//
            x = (XPosition);
            y = YPosition + TAM_HEIGHT / 2;
            x2 = (XPosition + TAM_HEIGHT / 2);
            y2 = YPosition + TAM_WIDTH ;
            sx = "左";
        }else{//
            x = (XPosition + TAM_HEIGHT );
            y = YPosition + TAM_HEIGHT / 2;
            sx = "右";
            x2 = (XPosition + TAM_HEIGHT / 2);
            y2 = YPosition;
        }
        //將圓形區域標記上
        for(int i = x-RADIUS; i < x + RADIUS; i ++){
            for(int j = y-RADIUS; j < y + RADIUS; j ++){
                if(Math.pow(Math.abs(x - i), 2) + Math.pow(Math.abs(y - j), 2) <= Math.pow(RADIUS, 2)){
                    //說明在圓內
                    blockData[i][j] = 2;
                }
            }
        }
        for(int i = x2-RADIUS; i < x2 + RADIUS; i ++){
            for(int j = y2-RADIUS; j < y2 + RADIUS; j ++){
                if(Math.pow(Math.abs(x2 - i), 2) + Math.pow(Math.abs(y2 - j), 2) <= Math.pow(RADIUS, 2)){
                    //說明在圓內
                    blockData[i][j] = 0;
                }
            }
        }
        return blockData;
    }
    /**
     * 隨機獲取一個圖片文件
     * @param filePath
     * @return
     * @throws Exception 
     */
    private File getRandomImage(String filePath) throws Exception {
        File file = new File(filePath);
        if(!file.exists()){
            throw new Exception("該文件路徑不對");
        }
        if(file.isDirectory()){
            File[] files = file.listFiles();
            if(files.length <= 0){
                throw new Exception("該文件夾內沒有文件!");
            }else{
                
                int index = random.nextInt(files.length);
                return files[index];
            }
        }else{
            return file;
        }
    }
}


思路啟發來源於網上對於圖片滑動驗證的原理,技術實現來源於自己對於下面博客的關鍵技術的提煉:
https://blog.csdn.net/MrSpirit/article/details/100653864


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM