zoukankan      html  css  js  c++  java
  • 图片滑动验证的实现

    有时候在页面登录或者注册的时候,为了防止不是机器人操作,会让用户手动来滑动图片验证。我在做项目时用到了这个功能,这里记录一下自己的想法和做法。

    实现的效果如图所示:

    好了,现在来说说想法。

    关于图片滑动验证一般是要前后端来交互的。首先是要后台处理好图片,然后将处理出来的图片返回到前台。后台随机抽取一张模板图片,也就是完整的图片,然后通过代码来操作将一块区域的图扣出来,将扣掉的地方填成灰色或者黑色,这样,素材就有了。

    在返回到前台时,要将图片转码为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
  • 相关阅读:
    ios 手写键盘闪退问题 UIKBBlurredKeyView candidateList
    ios NSURLErrorDomain Code=-1004 "未能连接到服务器。问题
    最牛B的编码套路
    watchOS开发—初步认识
    iOS开发拓展篇—蓝牙之CoreBlueTooth(BLE)
    iOS开发拓展篇—蓝牙之mutipeerConnectivity的使用
    iOS开发拓展篇—蓝牙之GameKit使用
    iOS开发拓展篇—ReactiveCocoa常见操作方法介绍(进阶篇)
    iOS开发拓展篇—ReactiveCocoa介绍(基础篇)
    iOS开发拓展篇—异常处理
  • 原文地址:https://www.cnblogs.com/1998xujinren/p/12334895.html
Copyright © 2011-2022 走看看