登陸圖片拖動驗證聯繫後臺

前段時間上級要我弄一個登陸時候的圖片拖動驗證。相似於嗶哩嗶哩登陸的那種。在網上找了一波,發現有的要錢,有的是直接前臺的拖動驗證操做的。可是不清楚究竟是在前端驗證仍是後端驗證安全點。想來想去我以爲得在後端,原諒我原本是後端的被趕鴨子上架寫前端潛意識以爲後端更安全。如今記錄一下。javascript

平常不變的SSM框架。css

思路:後端圖片裁剪轉爲base64(裁剪的X軸起點存在session,原本是想存redis)-->前端展現(獲取前端拖動的距離ajax進後臺與session中X軸對比)html

就在兩步。前端

首先是圖片裁剪類(在網上找的一位大佬的本身改了一下):java

package com.image.yanzhen;

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import sun.misc.BASE64Encoder;

public class ImageCut {
     /**
     * 源圖片路徑名稱如:c:\1.jpg
     */
    private String srcpath = "e:/poool.jpg";
    /**
     * 剪切圖片存放路徑名稱.如:c:\2.jpg
     */
    private String subpath = "e:/pool_end";
    /**
     * jpg圖片格式
     */
    private static final String IMAGE_FORM_OF_JPG = "jpg";
    /**
     * png圖片格式
     */
    private static final String IMAGE_FORM_OF_PNG = "png";
    /**
     * 剪切點x座標
     */
    private int x;

    /**
     * 剪切點y座標
     */
    private int y;

    /**
     * 剪切點寬度
     */
    private int width;

    /**
     * 剪切點高度
     */
    private int height;

    public ImageCut() {

    }

    public ImageCut(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public static void main(String[] args) throws Exception {
        ImageCut imageCut = new ImageCut(134, 0, 366, 366);
        imageCut.cut(imageCut.getSrcpath(), imageCut.getSubpath());
    }

    /**
     * 返回包含全部當前已註冊 ImageReader 的 Iterator,這些 ImageReader 聲稱可以解碼指定格式。
     * 參數:formatName - 包含非正式格式名稱 .(例如 "jpeg" 或 "tiff")等 。
     * 
     * @param postFix
     *            文件的後綴名
     * @return
     */
    public Iterator<ImageReader> getImageReadersByFormatName(String postFix) {
        switch (postFix) {
        case IMAGE_FORM_OF_JPG:
            return ImageIO.getImageReadersByFormatName(IMAGE_FORM_OF_JPG);
        case IMAGE_FORM_OF_PNG:
            return ImageIO.getImageReadersByFormatName(IMAGE_FORM_OF_PNG);
        default:
            return ImageIO.getImageReadersByFormatName(IMAGE_FORM_OF_JPG);
        }
    }

    /**
     * 對圖片裁剪,並把裁剪完蛋新圖片保存 。
     * @param srcpath 源圖片路徑
     * @param subpath 剪切圖片存放路徑
     * @throws IOException
     */
    public String cut(String srcpath, String subpath) throws IOException {
        FileInputStream is = null;
        ImageInputStream iis = null;
        try {
            // 讀取圖片文件
            is = new FileInputStream(srcpath);

            // 獲取文件的後綴名
            String postFix = getPostfix(srcpath);
            System.out.println("圖片格式爲:" + postFix);
            /*
             * 返回包含全部當前已註冊 ImageReader 的 Iterator,這些 ImageReader 聲稱可以解碼指定格式。
             * 參數:formatName - 包含非正式格式名稱 .(例如 "jpeg" 或 "tiff")等 。
             */
            Iterator<ImageReader> it = getImageReadersByFormatName(postFix);

            ImageReader reader = it.next();
            // 獲取圖片流
            iis = ImageIO.createImageInputStream(is);

            /*
             * <p>iis:讀取源.true:只向前搜索 </p>.將它標記爲 ‘只向前搜索’。
             * 此設置意味着包含在輸入源中的圖像將只按順序讀取,可能容許 reader 避免緩存包含與之前已經讀取的圖像關聯的數據的那些輸入部分。
             */
            reader.setInput(iis, true);

            /*
             * <p>描述如何對流進行解碼的類<p>.用於指定如何在輸入時從 Java Image I/O
             * 框架的上下文中的流轉換一幅圖像或一組圖像。用於特定圖像格式的插件 將從其 ImageReader 實現的
             * getDefaultReadParam 方法中返回 ImageReadParam 的實例。
             */
            ImageReadParam param = reader.getDefaultReadParam();

            /*
             * 圖片裁剪區域。Rectangle 指定了座標空間中的一個區域,經過 Rectangle 對象
             * 的左上頂點的座標(x,y)、寬度和高度能夠定義這個區域。
             */
            Rectangle rect = new Rectangle(x, y, width, height);

            // 提供一個 BufferedImage,將其用做解碼像素數據的目標。
            param.setSourceRegion(rect);
            /*
             * 使用所提供的 ImageReadParam 讀取經過索引 imageIndex 指定的對象,並將 它做爲一個完整的
             * BufferedImage 返回。
             */
            BufferedImage bi = reader.read(0, param);
            
            ByteArrayOutputStream CatBaos = new ByteArrayOutputStream();//io流
            ImageIO.write(bi, "jpg", CatBaos);//寫入流中
            byte[] CutBytes = CatBaos.toByteArray();//轉換成字節
            BASE64Encoder encoder = new BASE64Encoder();
            String CutPng_base64 =  encoder.encodeBuffer(CutBytes).trim();//轉換成base64串
            CutPng_base64 = CutPng_base64.replaceAll("\n", "").replaceAll("\r", "");//刪除 \r\n

//            String path = subpath + "_" + new Date().getTime() + "." + postFix;
//            
//            File file = new File(path);
//            File fileParent = file.getParentFile();
//            if(!fileParent.exists()){
//                System.out.println("自動建立文件");
//                fileParent.mkdirs(); 
//            } 
//                file.createNewFile();
//            
//            // 保存新圖片
//            ImageIO.write(bi, postFix, new File(path));
            
            return CutPng_base64;
        } finally {
            if (is != null)
                is.close();
            if (iis != null)
                iis.close();
        }

    }

    /**
     * 獲取inputFilePath的後綴名,如:"e:/test.pptx"的後綴名爲:"pptx"<br>
     * 
     * @param inputFilePath
     * @return
     */
    public String getPostfix(String inputFilePath) {
        return inputFilePath.substring(inputFilePath.lastIndexOf(".") + 1);
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getSrcpath() {
        return srcpath;
    }

    public void setSrcpath(String srcpath) {
        this.srcpath = srcpath;
    }

    public String getSubpath() {
        return subpath;
    }

    public void setSubpath(String subpath) {
        this.subpath = subpath;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

再下來就是裁剪了(代碼辣雞,你們看看就好)jquery

package com.image.yanzhen;

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

import javax.imageio.ImageIO;

import com.attendance.utils.PathUtil;

import sun.misc.BASE64Encoder;

public class ImgCutTest {
    
    public Map<Object,Object> getBase() throws FileNotFoundException, IOException {
        Map<Object,Object> map = new HashMap<Object,Object>();
        Random rand = new Random();
        
        int ImgIndex = rand.nextInt(6); //生成0-6之內的隨機數
        if(ImgIndex==0){
            ImgIndex = 1;
        }
        
        String path = PathUtil.getClasspath()+"baseImg/"+ImgIndex+".jpg";
        System.out.println("圖片路徑-->"+path);
        File picture = new File(path);
        BufferedImage sourceImg = ImageIO.read(new FileInputStream(picture));
        
        /*************************裁剪圖片得到base64*************************/
        int CJX = rand.nextInt(700); //生成0-700之內的隨機數
        int CJY = rand.nextInt(480); //生成0-480之內的隨機數
        System.out.println("隨機x起點-->"+CJX);
        System.out.println("隨機y起點-->"+CJY);
        if(CJX<200){
            System.out.println("太少了");
            CJX = CJX + 200;
            System.out.println("增長後-->"+CJX);
        }
        int CutX1 = CJX;        //裁剪X軸起點
        int CutY1 = CJY;        //裁剪Y軸起點
        int CutW1 = 200;    //裁剪寬度
        int CutH1 = 120;    //裁剪高度
        System.out.println(CutX1+"、"+CutY1+"、"+CutW1+"、"+CutH1);
        ImageCut imageCut1 = new ImageCut(CutX1, CutY1, CutW1, CutH1);
        String CutPng_base64 = imageCut1.cut(path, null);
        /********************************************************/
        
        /*************************生成數組*************************/
        int[][] data = new int[sourceImg.getWidth()][sourceImg.getHeight()];
        for (int i=0;i<sourceImg.getWidth();i++){//1280
            for(int j=0;j<sourceImg.getHeight();j++){//720
                if(i<CJX+200&&i>=CJX&&j<CJY+120&&j>CJY){
                    data[i][j]=1;
                }else {
                    data[i][j]=0;
                }
            }
        }
        /*********************************************************/
        
        /************************圖片局部變黑************************/
        for (int i = 0; i < sourceImg.getWidth(); i++) {
            for (int j = 0; j < sourceImg.getHeight(); j++) {
                int rgb = data[i][j];
                // 原圖中對應位置變色處理
                
                int rgb_ori = sourceImg.getRGB(i,  j);
                
                if (rgb == 1) {
                    //顏色處理
                    int r = (0XFF000000 & rgb_ori);
                    int g = (0XFF000000 & (rgb_ori >> 8));
                    int b = (0XFF000000 & (rgb_ori >> 16));
                    int Gray = (r*2 + g*5 + b*1) >> 3;
                    
                    //原圖對應位置顏色變化
                    sourceImg.setRGB( i, j, Gray);
                }
            }
        }
        /**********************************************************/
        
        /************************陰影圖片轉base64************************/
        BASE64Encoder encoder = new BASE64Encoder();
        ByteArrayOutputStream YYbaos = new ByteArrayOutputStream();//io流
        ImageIO.write(sourceImg, "jpg", YYbaos);//寫入流中
        byte[] bytes = YYbaos.toByteArray();//轉換成字節
        String YYPng_base64 =  encoder.encodeBuffer(bytes).trim();//轉換成base64串
        YYPng_base64 = YYPng_base64.replaceAll("\n", "").replaceAll("\r", "");//刪除 \r\n
        /**********************************************************/
        
        map.put("CJX", CJX);                    //裁剪開始X座標
        map.put("CJY", CJY);                    //裁剪開始Y座標
        map.put("YYPng_base64", YYPng_base64);    //陰影圖片base
        map.put("CutPng_base64", CutPng_base64);//裁剪圖片base
        
        return map;
    }
}

最重要的就是上面ajax

這個兩個了。redis

而後是controller調用圖片裁剪並返回前臺json

/**
     * 去圖片驗證頁面
     * @param session
     * @return
     * @throws IOException
     */
    @RequestMapping(value="/GoUploadImg.do",method = RequestMethod.GET)
    @ResponseBody
    public Object IndexGoLogin(HttpSession session) throws IOException{
        System.out.println("進入圖片上傳頁面");
        Map<Object,Object> map = new HashMap<Object,Object>();
        map = new ImgCutTest().getBase();
        String uuid = UuidUtil.get32UUID();
        //前臺圖片展現爲原圖的一半
        int CJX = (int) map.get("CJX")/2;
        session.setAttribute(uuid, CJX);
        map.put("uuid", uuid);
        return JSONArray.toJSONString(map);
    }

接下來就是前臺的一些展現,和拖動的一些東西。後端

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>

    <script type="text/javascript" charset="utf-8" src="statics/jquery-3.2.1.min.js"></script>
    <link rel="stylesheet" href="statics/layui/css/layui.css" type="text/css"/>
    <script type="text/javascript" src="statics/layui/layui.js"></script>
    
<script>
    function getYZ(){    
        $.ajax({
            type:"GET",
            url:"GoUploadImg.do",
            data:{},
            dataType:"json",
            success:function(data){
                console.log(data);
                $("#YZUUID").val(data.uuid);
                $("#JQX").html(data.CJX);
                $("#JQY").html(data.CJY);
                $("#JQIMG").html("<img src=\"data:image/jpeg;base64,"+data.CutPng_base64+"\"/>");
                $("#YYIMG").html("<img src=\"data:image/jpeg;base64,"+data.YYPng_base64+"\"/>");
                
                $("#YZDIVIMG").html("<img style=\"max-width:100%;max-height:100%;\" src=\"data:image/jpeg;base64,"+data.YYPng_base64+"\"/>");
                $("#CJDIVIMG").html("<img style=\"max-width:100%;max-height:100%;\" src=\"data:image/jpeg;base64,"+data.CutPng_base64+"\"/>");
                
                //移動上下位置
                var MarTop = data.CJY/2+20+"px";
                $("#CJDIVIMG").css('margin-top',MarTop);
                
            },error:function(data){//當訪問是,404,500,等非200錯誤狀態碼
                alert("親的網絡忽然出錯了呢!請稍後刷新再操做!");
            }
        });
    }
    
    
    $(document).ready(function(){
        
        var w = 450;
        var PL_Size = 100;    //缺失拼圖的大小
        var padding = 0;    //缺失拼圖與邊框的距離
        
        // 滑塊拖動
        var moveStart = '';//定義一個鼠標按下的X軸值
        //鼠標按下
        $(".slider-btn").mousedown(function(e){
            e = e || window.event;
            // 鼠標在滑塊按下切換滑塊背景
            $(this).css({
                "background-position":"0 -216px"
            });
            moveStart = e.pageX;//記錄鼠標按下時的座標 X軸值
        });
        //鼠標拖動(這裏使用全局監聽鼠標移動的事件)
        onmousemove = function(e) {
            e = e || window.event;
            var moveX = e.pageX;//監聽鼠標的位置
            var d = moveX-moveStart;    //鼠標按住後在X軸上移動的距離
            if(moveStart == '') {
                // console.log('未拖動滑塊');
            } else {
                if(d<0 || d>(w-padding-PL_Size)) {
                    // console.log('超過範圍');
                } else {
                    var OtherD = d+20;
                    $(".slider-btn").css({
                        "left":d + 'px',
                        "transition":"inherit"
                    });
                    $("#CJDIVIMG").css({
                        "left":OtherD + 'px',
                        "transition":"inherit"
                    });
                }
            }
        };
        
          //鼠標鬆開 (這裏使用全局監聽鼠標鬆開的事件)
        onmouseup = function (e) {
            e = e || window.event;
            var moveEnd_X = e.pageX - moveStart;//鬆開鼠標後滑塊移動的距離
            if(moveStart == '') {

            } else {
                var uuid = $("#YZUUID").val();
                $.ajax({
                    type:"POST",
                    url:"YanZhenX.do",
                    data:{uuid:uuid,moveEnd_X:moveEnd_X},
                    dataType:"json",
                    success:function(data){
                        console.log(data);
                        if(data.YZ=="yes"){
                            $("#YZDExpress").html("<i class=\"layui-icon layui-icon-ok-circle\" style=\"font-size: 18px; color: green;\"><i style=\"font-size:15px;\">驗證經過&nbsp;</i></i><font style=\"font-size:15px;\">你的速度飛快,超過絕大多數人</font>");
                        }else{
                            $("#YZDExpress").html("<i class=\"layui-icon layui-icon-close-fill\" style=\"font-size: 18px; color: red;\"><i style=\"font-size:15px;\">驗證失敗&nbsp;</i></i><font style=\"font-size:15px;\">拖動滑塊將懸浮圖像正確拼接</font>");
                        }
                    },error:function(data){//當訪問是,404,500,等非200錯誤狀態碼
                        alert("親的網絡忽然出錯了呢!請稍後刷新再操做!");
                    }
                });
            }
            setTimeout(function () {
                $(".slider-btn").css({
                    "left":'0',
                    "transition":"left 0.5s"
                });
                $("#CJDIVIMG").css({
                    "left":'20px',
                    "transition":"left 0.5s"
                });
                $("#YZDExpress").html("");
            },1000);
            $(".slider-btn").css({
                "background-position":"0 -84px"
            });
            moveStart = '';//  清空上一次鼠標按下時的座標X軸值;
        }
    });
  </script>
  <style>
      .YZDIV{
          width: 490px;
          height:360px;
          border: solid #E7E3DA thin;
          background-color: #F4ECE3;
          border-radius: 20px;
          text-align: center;
      }
      .YYIMG{
          width: 450px;
          height:300px;
          border: none;
          background-color: #ECE4DD;
          margin:0 auto;
          margin-top:20px;
      }
      .CJIMG{
          width: 100px;
          height:60px;
          border: #D3D664 solid thin;
          background-color: #ECE4DD;
          z-index: 200;
          position: absolute;
          left:20px;
      }
      .slider-btn {
        position:absolute;
        width:44px;
        height:44px;
        left:0;
        top:-7px;
        z-index:12;
        cursor:pointer;
        background-image:url("statics/image/sprite.3.2.0.png");
        background-position:0 -84px;
        transition:inherit;
    }
    .layui-icon-refresh-3:HOVER {
        color: green;
    }
    
  </style>
<body onload="getYZ()">
<!--     <button onclick="getYZ()">驗證素材</button> -->
    
    <input type="hidden" id="YZUUID"/>
    
    <div class="YZDIV">
        
        <div class="CJIMG" id="CJDIVIMG"></div>
            
        <div class="YYIMG" id="YZDIVIMG"></div>
        
        <i onclick="getYZ()" class="layui-icon layui-icon-refresh-3" style="font-size: 18px;float: left;line-height: 40px;margin-left: 12px;cursor: pointer;" title="刷新驗證"></i>
          
        <i id="YZDExpress" style="line-height: 40px;"></i>
    </div>
    <br/>
    <div style="position:relative;width:490px;">
        <div style="border:1px solid #c3c3c3;border-radius:24px;background:#ece4dd;box-shadow:0 1px 1px rgba(12,10,10,0.2) inset;">
            <p style="-moz-user-select: none; -khtml-user-select: none; user-select: none;font-size:12px;color: #486c80;line-height:28px;margin:0;text-align:right;padding-right:22px;text-align: center;">按住左邊滑塊,拖動完成上方拼圖</p>
        </div>
        <div class="slider-btn" id="ANNIU"></div>
    </div>
    
    <br/><br/><br/><br/>
    
    截取X起點:<div id="JQX"></div><br/>
    截取Y起點:<div id="JQY"></div><br/>
    截取圖片:<div id="JQIMG"></div><br/>
    陰影圖片:<div id="YYIMG"></div><br/>
    
</body>
</html>

這邊拖動在鼠標鬆開的時候會回後臺進行驗證

/**
     * 滑動驗證
     * @param uuid    標識符
     * @param moveEnd_X    滑動距離
     * @param session
     * @return
     * @throws IOException
     */
    @RequestMapping(value="/YanZhenX.do",method = RequestMethod.POST)
    @ResponseBody
    public Object YanZhenX(@RequestParam String uuid,@RequestParam int moveEnd_X,HttpSession session) throws IOException{
        System.out.println("進行驗證");
        Map<Object,Object> map = new HashMap<Object,Object>();
        int CJX = (int) session.getAttribute(uuid);
        System.out.println("uuid-->"+uuid);
        System.out.println("滑動x距離-->"+moveEnd_X);
        System.out.println("裁剪距離-->"+CJX);
        if(moveEnd_X>CJX-3&&moveEnd_X<CJX+3){    //誤差在3之類
            System.out.println("拼接成功");
            map.put("YZ", "yes");
        }else{
            System.out.println("誤差過大");
            map.put("YZ", "no");
        }
        
        return JSONArray.toJSONString(map);
    }

大體的就這麼個樣子。

放幾張效果圖

好了,就這些了。有問題請多多指點。對了那些圖片我都是直接選的900*600的

源碼也放一下下。你們想看的能夠下載,雖然基本的代碼都在上面了

https://download.csdn.net/download/qq_38196854/10816634

相關文章
相關標籤/搜索