登录图片拖动验证联系后台

前段时间上级要我弄一个登录时候的图片拖动验证。类似于哔哩哔哩登录的那种。在网上找了一波,发现有的要钱,有的是直接前台的拖动验证操作的。但是不清楚到底是在前端验证还是后端验证安全点。想来想去我觉得得在后端,原谅我本来是后端的被赶鸭子上架写前端潜意识觉得后端更安全。现在记录一下。

日常不变的SSM框架。

思路:后端图片裁剪转为base64(裁剪的X轴起点存在session,本来是想存redis)-->前端展示(获取前端拖动的距离ajax进后台与session中X轴对比)

就在两步。

首先是图片裁剪类(在网上找的一位大佬的自己改了一下):

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

再下来就是裁剪了(代码辣鸡,大家看看就好)

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

最重要的就是上面

这个两个了。

然后是controller调用图片裁剪并返回前台

/**
     * 去图片验证页面
     * @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 οnclick="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

 

转载于:https://www.cnblogs.com/ld199848/p/10038562.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值