SSM后台图片滑动验证

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

日常不变的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 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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值