Java实现滑块拼图验证码

本文实例为大家分享了Java实现滑块拼图验证码的具体代码,供大家参考,具体内容如下

1、后端随机生成抠图和带有抠图阴影的背景图片,后台保存随机抠图位置坐标
2、前端实现滑动交互,将抠图拼在抠图阴影之上,获取到用户滑动距离值,比如以下示例

3、前端将用户滑动距离值传入后端,后端校验误差是否在容许范围内。

这里单纯校验用户滑动距离是最基本的校验,出于更高的安全考虑,可能还会考虑用户滑动的整个轨迹,用户在当前页面的访问行为等。这些可以很复杂,甚至借助到用户行为数据分析模型,最终的目标都是增加非法的模拟和绕过的难度。这些有机会可以再归纳总结常用到的方法,本文重点集中在如何基于Java来一步步实现滑动验证码的生成。

可以看到,滑动图形验证码,重要有两个图片组成,抠块和带有抠块阴影的原图,这里面有两个重要特性保证被暴力破解的难度:抠块的形状随机和抠块所在原图的位置随机。这样就可以在有限的图集中制造出随机的、无规律可寻的抠图和原图的配对。

用代码如何从一张大图中抠出一个有特定随机形状的小图呢?

第一步,先确定一个抠出图的轮廓,方便后续真正开始执行图片处理操作

图片是有像素组成,每个像素点对应一种颜色,颜色可以用RGB形式表示,外加一个透明度,把一张图理解成一个平面图形,左上角为原点,向右x轴,向下y轴,一个坐标值对应该位置像素点的颜色,这样就可以把一张图转换成一个二维数组。基于这个考虑,轮廓也用二维数组来表示,轮廓内元素值为1,轮廓外元素值对应0。

这时候就要想这个轮廓形状怎么生成了。有坐标系、有矩形、有圆形,没错,用到数学的图形函数。典型用到一个圆的函数方程和矩形的边线的函数,类似:

(x-a)²+(y-b)²=r²中,有三个参数a、b、r,即圆心坐标为(a,b),半径r。这些将抠图放在上文描述的坐标系上很容易就图算出来具体的值。

示例代码如下:

static int targetWidth = 55;//小图长
static int targetHeight = 45;//小图宽
static int circleR = 8;//半径
static int r1 = 4;//距离点

    /**
     * @Createdate: 2019年1月24日上午10:52:42
     * @Title: getBlockData
     * @Description: 生成小图轮廓
     * @author zhoujin
     * @return int[][]
     * @throws
     */
    private static int[][] getBlockData() {
        int[][] data = new int[targetWidth][targetHeight];
        double x2 = targetWidth -circleR; //47

        //随机生成圆的位置
        double h1 = circleR + Math.random() * (targetWidth-3*circleR-r1);
        double po = Math.pow(circleR,2); //64

        double xbegin = targetWidth - circleR - r1;
        double ybegin = targetHeight- circleR - r1;

        //圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆
        //计算需要的小图轮廓,用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色
        for (int i = 0; i < targetWidth; i++) {
            for (int j = 0; j < targetHeight; j++) {
                double d2 = Math.pow(j - 2,2) + Math.pow(i - h1,2);
                double d3 = Math.pow(i - x2,2) + Math.pow(j - h1,2);
                if ((j <= ybegin && d2 < po)||(i >= xbegin && d3 > po)) {
                        data[i][j] = 0;
                }  else {
                        data[i][j] = 1;
                }
            }
        }
        return data;
    }

第二步,有这个轮廓后就可以依据这个二维数组的值来判定抠图并在原图上抠图位置处加阴影。

操作如下:

/**
     *
     * @Createdate: 2019年1月24日上午10:51:30
     * @Title: cutByTemplate
     * @Description: 有这个轮廓后就可以依据这个二维数组的值来判定抠图并在原图上抠图位置处加阴影,
     * @author zhoujin
     * @param oriImage  原图
     * @param targetImage  抠图拼图
     * @param templateImage 颜色
     * @param x
     * @param y void
     * @throws
     */
    private static void cutByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] templateImage, int x, int y){
        int[][] martrix = new int[3][3];
        int[] values = new int[9];
        //创建shape区域
        for (int i = 0; i < targetWidth; i++) {
            for (int j = 0; j < targetHeight; j++) {
                int rgb = templateImage[i][j];
                // 原图中对应位置变色处理
                int rgb_ori = oriImage.getRGB(x + i, y + j);

                if (rgb == 1) {
                    targetImage.setRGB(i, j, rgb_ori);

                    //抠图区域高斯模糊
                    readPixel(oriImage, x + i, y + j, values);
                    fillMatrix(martrix, values);
                    oriImage.setRGB(x + i, y + j, avgMatrix(martrix));
                }else{
                    //这里把背景设为透明
                    targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
                }
            }
        }
    }

   private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
        int xStart = x - 1;
        int yStart = y - 1;
        int current = 0;
        for (int i = xStart; i < 3 + xStart; i++)
            for (int j = yStart; j < 3 + yStart; j++) {
                int tx = i;
                if (tx < 0) {
                    tx = -tx;

                } else if (tx >= img.getWidth()) {
                    tx = x;
                }
                int ty = j;
                if (ty < 0) {
                    ty = -ty;
                } else if (ty >= img.getHeight()) {
                    ty = y;
                }
                pixels[current++] = img.getRGB(tx, ty);

            }
    }

    private static void fillMatrix(int[][] matrix, int[] values) {
        int filled = 0;
        for (int i = 0; i < matrix.length; i++) {
            int[] x = matrix[i];
            for (int j = 0; j < x.length; j++) {
                x[j] = values[filled++];
            }
        }
    }

    private static int avgMatrix(int[][] matrix) {
        int r = 0;
        int g = 0;
        int b = 0;
        for (int i = 0; i < matrix.length; i++) {
            int[] x = matrix[i];
            for (int j = 0; j < x.length; j++) {
                if (j == 1) {
                    continue;
                }
                Color c = new Color(x[j]);
                r += c.getRed();
                g += c.getGreen();
                b += c.getBlue();
            }
        }
        return new Color(r / 8, g / 8, b / 8).getRGB();
    }

经过前面两步后,就得到了抠图和带高斯模糊抠图阴影的原图。返回生成的抠图和带阴影的大图base64码及抠图坐标。

/**
     * @Description: 读取本地图片,生成拼图验证码
     * @author zhoujin
     * @return Map<String,Object>  返回生成的抠图和带抠图阴影的大图 base64码及抠图坐标
     */
    public static Map<String,Object> createImage(File file, Map<String,Object> resultMap){
        try {
            BufferedImage oriImage = ImageIO.read(file);
            Random random = new Random();
            //X轴距离右端targetWidth  Y轴距离底部targetHeight以上
            int widthRandom = random.nextInt(oriImage.getWidth()-  2*targetWidth) + targetWidth;
            int heightRandom = random.nextInt(oriImage.getHeight()- targetHeight);
            logger.info("原图大小{} x {},随机生成的坐标 X,Y 为({},{})",oriImage.getWidth(),oriImage.getHeight(),widthRandom,heightRandom);

            BufferedImage targetImage= new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_4BYTE_ABGR);
            cutByTemplate(oriImage,targetImage,getBlockData(),widthRandom,heightRandom);

            resultMap.put("bigImage", getImageBASE64(oriImage));//大图
            resultMap.put("smallImage", getImageBASE64(targetImage));//小图
            resultMap.put("xWidth",widthRandom);
            resultMap.put("yHeight",heightRandom);
        } catch (Exception e) {
            logger.info("创建图形验证码异常",e);
        } finally{
            return resultMap;
        }
    }

    /**
     * @Description: 读取网络图片,生成拼图验证码
     * @author zhoujin
     * @return Map<String,Object>  返回生成的抠图和带抠图阴影的大图 base64码及抠图坐标
     */
    public static Map<String,Object> createImage(String imgUrl, Map<String,Object> resultMap){
        try {
            //通过URL 读取图片
            URL url = new URL(imgUrl);
            BufferedImage bufferedImage = ImageIO.read(url.openStream());
            Random rand = new Random();
            int widthRandom = rand.nextInt(bufferedImage.getWidth()-  targetWidth - 100 + 1 ) + 100;
            int heightRandom = rand.nextInt(bufferedImage.getHeight()- targetHeight + 1 );
            logger.info("原图大小{} x {},随机生成的坐标 X,Y 为({},{})",bufferedImage.getWidth(),bufferedImage.getHeight(),widthRandom,heightRandom);

            BufferedImage target= new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_4BYTE_ABGR);
            cutByTemplate(bufferedImage,target,getBlockData(),widthRandom,heightRandom);
            resultMap.put("bigImage", getImageBASE64(bufferedImage));//大图
            resultMap.put("smallImage", getImageBASE64(target));//小图
            resultMap.put("xWidth",widthRandom);
            resultMap.put("yHeight",heightRandom);
        } catch (Exception e) {
            logger.info("创建图形验证码异常",e);
        } finally{
            return resultMap;
        }
    }

    /**
     * @Title: getImageBASE64
     * @Description: 图片转BASE64
     * @author zhoujin
     * @param image
     * @return
     * @throws IOException String
     */
    public static String getImageBASE64(BufferedImage image) throws IOException {
        byte[] imagedata = null;
        ByteArrayOutputStream bao=new ByteArrayOutputStream();
        ImageIO.write(image,"png",bao);
        imagedata=bao.toByteArray();
        BASE64Encoder encoder = new BASE64Encoder();
        String BASE64IMAGE=encoder.encodeBuffer(imagedata).trim();
        BASE64IMAGE = BASE64IMAGE.replaceAll("\r|\n", "");  //删除 \r\n
        return BASE64IMAGE;
    }

控制层代码实现及校验验证码:

/**
     * @param @return 参数说明
     * @return BaseRestResult 返回类型
     * @Description: 生成滑块拼图验证码
     */
    @RequestMapping(value = "/getImageVerifyCode.do", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
    public BaseRestResult getImageVerifyCode() {
        Map<String, Object> resultMap = new HashMap<>();
        //读取本地路径下的图片,随机选一条
        File file = new File(this.getClass().getResource("/image").getPath());
        File[] files = file.listFiles();
        int n = new Random().nextInt(files.length);
        File imageUrl = files[n];
        ImageUtil.createImage(imageUrl, resultMap);

        //读取网络图片
        //ImageUtil.createImage("/7986d66f29bfeb6015aaaec33d33fcd1d875ca16316f-2bMHNG_fw658",resultMap);
        session.setAttribute("xWidth", resultMap.get("xWidth"));
        resultMap.remove("xWidth");
        resultMap.put("errcode", 0);
        resultMap.put("errmsg", "success");
        return new BaseRestResult(resultMap);
    }

    /**
     * 校验滑块拼图验证码
     *
     * @param moveLength 移动距离
     * @return BaseRestResult 返回类型
     * @Description: 生成图形验证码
     */
    @RequestMapping(value = "/verifyImageCode.do", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
    public BaseRestResult verifyImageCode(@RequestParam(value = "moveLength") String moveLength) {
        Double dMoveLength = Double.valueOf(moveLength);
        Map<String, Object> resultMap = new HashMap<>();
        try {
            Integer xWidth = (Integer) session.getAttribute("xWidth");
            if (xWidth == null) {
                resultMap.put("errcode", 1);
                resultMap.put("errmsg", "验证过期,请重试");
                return new BaseRestResult(resultMap);
            }
            if (Math.abs(xWidth - dMoveLength) > 10) {
                resultMap.put("errcode", 1);
                resultMap.put("errmsg", "验证不通过");
            } else {
                resultMap.put("errcode", 0);
                resultMap.put("errmsg", "验证通过");
            }
        } catch (Exception e) {
            throw new EsServiceException(e.getMessage());
        } finally {
            session.removeAttribute("xWidth");
        }
        return new BaseRestResult(resultMap);
}

前端显示图片代码:

<img src="data:image/png;base64,+返回的base64图片码" alt="抠图">
<img src="data:image/png;base64,+返回的base64图片码" alt="带抠图阴影的原图">

至此后台java实现滑块验证码关键代码完成!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java实现仿淘宝滑动验证码研究代码详解

    通过下面一张图看下要实现的功能,具体详情如下所示: 现在我就来介绍些软件的其它功能.希望大家有所受益. 模拟人为搜索商品 在刷单的时候,不能直接拿到一个商品网址就进入购买页面吧,得模拟人为搜索. 在这一个过程中有两个难点: 1)商品列表的异步加载 ; 2)翻页并且截图; 在园子里,我就不在关公面前耍大刀了. 直接上关键代码: i:搜索商品,并且翻页 public bool? SearchProduct(TaskDetailModel taskDetailData) { bool? result

  • Java实现滑动验证码的示例代码

    最近滑动验证码在很多网站逐步流行起来,一方面对用户体验来说,比较新颖,操作简单,另一方面相对图形验证码来说,安全性并没有很大的降低.当然到目前为止,没有绝对的安全验证,只是不断增加攻击者的绕过成本. 接下来分析下滑动验证码的核心流程: 后端随机生成抠图和带有抠图阴影的背景图片,后台保存随机抠图位置坐标 前端实现滑动交互,将抠图拼在抠图阴影之上,获取到用户滑动距离值,比如以下示例 前端将用户滑动距离值传入后端,后端校验误差是否在容许范围内. 这里单纯校验用户滑动距离是最基本的校验,出于更高的安全考

  • Java实现滑块拼图验证码

    本文实例为大家分享了Java实现滑块拼图验证码的具体代码,供大家参考,具体内容如下 1.后端随机生成抠图和带有抠图阴影的背景图片,后台保存随机抠图位置坐标 2.前端实现滑动交互,将抠图拼在抠图阴影之上,获取到用户滑动距离值,比如以下示例 3.前端将用户滑动距离值传入后端,后端校验误差是否在容许范围内. 这里单纯校验用户滑动距离是最基本的校验,出于更高的安全考虑,可能还会考虑用户滑动的整个轨迹,用户在当前页面的访问行为等.这些可以很复杂,甚至借助到用户行为数据分析模型,最终的目标都是增加非法的模拟

  • Android 简单的实现滑块拼图验证码功能

    实现滑块拼图验证码功能之前已经写过一篇了,上一篇使用的是自定义控件的方式实现这个功能,主要还是想让童鞋们知其然更知其所以然,还没看的童鞋可以先看看Android实现滑块拼图验证码功能这篇. 在项目的开发过程中,时间比较紧急,通过自定义的方式很显然需要耗费很多时间去写,所以我们需要使用更简单的方式实现,这样会帮我们节省很多时间去解决其它的问题,使用依赖库的方式显然是最节省时间的,下面我们来看看是怎么实现的吧! 本篇主要从两方面进行介绍: 1.使用依赖库实现最终的功能: 2.依赖库的介绍: 实现过程

  • Android实现滑块拼图验证码功能

    滑块拼图验证码应该算是很常见的功能了,验证码是可以区分用户是人还是机器.可以防止破解密码.刷票等恶意行为.本文将介绍Android拼图滑块验证码控件的实现过程.希望能帮助到大家. 先看最终的效果图: 本文只是做了个Demo,并没有加入到实际的项目中,所以各位童鞋可以根据自己的需求就行修改即可. 一.实现步骤: 1.定义自定义属性: 2.确认目标位置,这里使用的是阴影图片来遮盖背景图片: 3.创建与目标位置相结合的滑块图片: 4.设置目标阴影图片和滑块图片可以随机旋转,并保持一致: 5.创建拖拽条

  • python3 使用OpenCV计算滑块拼图验证码缺口位置(场景示例)

    前言 滑块拼图验证码的失败难度在于每次图片上缺口位置不一样,需识别图片上拼图的缺口位置,使用python的OpenCV库来识别到 环境准备 pip 安装 opencv-python pip installl opencv-python OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,提供了很多处理图片.视频的方法. OpenCV库提供了一个方法(matchTemplate()):从一张较大的图片中搜索一张较小图片,计算出这张大图上各

  • Python实现滑块拼图验证码详解

    目录 初级版滑块拼图验证码 补充知识点 高级版滑动拼图验证码 滑动拼图验证码可以算是滑块验证码的进阶版本,其验证机制相对复杂.本节将介绍两种滑动拼图验证码:初级版和高级版本. 初级版滑块拼图验证码 初级版滑动拼图验证码是在普通滑块验证码的基础上增加了随机的滑动距离,用户需要根据拼图的缺口位置来决定滑块的滑动位置. 如下左图所示为一个滑块拼图验证码的起始状态,注意此时还没有显示拼图和缺口.单击滑块后就会出现拼图和缺口,如下右图所示.之后会利用这一特性来找到拼图和缺口的位置. 下面开始编写代码.首先

  • vue+elementui实现拖住滑块拼图验证

    vue拖住滑块拼图验证,以下是cavas直接写的滑块拼图验证码,直接复制引用即可 <template>   <div id="puzzle" ref="puzzle" style="display:inline-block;">   <!-- :style="'padding:' + 16*scale + 'px ' + 16*scale + 'px ' + 28*scale + 'px;border-ra

  • html+jQuery实现拖动滑块图片拼图验证码插件【移动端适用】

    电脑和手机移动端都适用的jQuery拖动滑块图片拼图验证码插件,通过鼠标拖动或触屏滑动填充拼图来进行安全验证,点击刷新可以更换当前待验证的图片. HTML & css: <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content=&qu

  • Vue实现滑动拼图验证码功能

    缘由:之前看哔哩哔哩官网登录的时候有一个拼图验证码,很好奇怎么去实现.然后就想着自己弄一个.先给大家看我的最终效果.后面再一点点拆解代码. 为什么想着写这个功能呢,主要在于拼图验证码在前端这里会比较复杂并且深入.相比文字拼写,12306的图片验证码都没有拼图验证码对前端的要求来的复杂,和难. 我总结下知识点: 1.弹窗功能 2.弹窗基于元素定位 3.元素拖动 4.canvas绘图 5.基础逻辑 一.弹窗和弹窗组件 抱歉,这里我偷懒了直接用了elementUI的el-popover组件,所以小伙伴

  • js+canvas实现滑动拼图验证码功能

    上图为网易云盾的滑动拼图验证码,其应该有一个专门的图片库,裁剪的位置是固定的.我的想法是,随机生成图片,随机生成位置,再用canvas裁剪出滑块和背景图.下面介绍具体步骤. 首先随便找一张图片渲染到canvas上,这里#canvas作为画布,#block作为裁剪出来的小滑块. <canvas width="310" height="155" id="canvas"></canvas> <canvas width=&q

  • Python+selenium破解拼图验证码的脚本

    目录 实现思路 核心代码 实现思路 很多网站都有拼图验证码 1.首先要了解拼图验证码的生成原理 2.制定破解计划,考虑其可能性和成功率. 3.编写脚本 很多网站的拼图验证码都是直接借助第三方插件,也就是一类一种解法. 笔者遇到的这种拼图验证码实际上是多个小碎片经过重新组合成的一张整体,首先要在网站上抓取这种小碎片图片并下载到本地 我们先捋一捋大体思路: 获取所有碎片图片----找出他们的排列顺序逻辑-----找出他们中含有颜色深的真正位置的那个小碎块的序号-----根据每块碎片的宽度和上下和这个

随机推荐