一步步实现滑动验证码(拼图验证码),Java图片处理关键代码

摘要:
最近,滑动验证码在许多网站上流行起来。一方面,它相对较新,并且易于操作以获得用户体验。另一方面,与图形验证码相比,安全性并没有大幅降低。当然,到目前为止,还没有绝对的安全验证,但攻击者的绕过成本正在增加。这些是总结常用方法的机会。本文重点讨论如何基于Java逐步生成滑动验证代码。通常,有两件事要做。一种是模糊图像以增加机器识别的难度,另一种是以相同的质量适当压缩图像。

最近滑动验证码在很多网站逐步流行起来,一方面对用户体验来说,比较新颖,操作简单,另一方面相对图形验证码来说,安全性并没有很大的降低。当然到目前为止,没有绝对的安全验证,只是不断增加攻击者的绕过成本。

接下来分析下滑动验证码的核心流程:

  1. 后端随机生成抠图和带有抠图阴影的背景图片,后台保存随机抠图位置坐标
  2. 前端实现滑动交互,将抠图拼在抠图阴影之上,获取到用户滑动距离值,比如以下示例一步步实现滑动验证码(拼图验证码),Java图片处理关键代码第1张
  3. 前端将用户滑动距离值传入后端,后端校验误差是否在容许范围内。

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

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

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

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

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

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

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

示例代码如下:

private int[][] getBlockData() {
        int[][] data = new int[targetLength][targetWidth];
        double x2 = targetLength-circleR-2;
        //随机生成圆的位置
        double h1 = circleR + Math.random() * (targetWidth-3*circleR-r1);
        double po = circleR*circleR;
        double xbegin = targetLength-circleR-r1;
        double ybegin = targetWidth-circleR-r1;
        for (int i = 0; i < targetLength; i++) {
            for (int j = 0; j < targetWidth; j++) {
                //右边○
                double d3 = Math.pow(i - x2,2) + Math.pow(j - h1,2);
                if (d1 <=po
                        || (j >= ybegin && d2 >=po)
                        || (i >= xbegin && d3 >=po)
                        ) {
                    data[i][j] = 0;
                }  else{
                    data[i][j] = 1;
                }
            }
        }
        returndata;
    }

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

操作如下:

private void cutByTemplate(BufferedImage oriImage,BufferedImage targetImage, int[][] templateImage, intx,
            inty){
        for (int i = 0; i < targetLength; i++) {
            for (int j = 0; j < targetWidth; j++) {
                int rgb =templateImage[i][j];
                //原图中对应位置变色处理
                int rgb_ori = oriImage.getRGB(x + i, y +j);
                if (rgb == 1) {
                    //抠图上复制对应颜色值
                    targetImage.setRGB(i, y +j, rgb_ori);
                    int r = (0xff &rgb_ori);
                    int g = (0xff & (rgb_ori >> 8));
                    int b = (0xff & (rgb_ori >> 16)));
                    rgb_ori = r + (g << 8) + (b << 16) + (200 << 24);
                    //原图对应位置颜色变化
                    oriImage.setRGB(x + i, y +j, rgb_ori);
                } 
            }
        }
    }

经过前面两步后,就得到了抠图和带抠图阴影的原图。为增加混淆和提高网络加载效果,还需要对图片做进一步处理。一般有两件事需要做,一对图片做模糊处理增加机器识别难度,二做适当同质量压缩。模糊处理很容易想到高斯模糊,原理很好理解,大家可以去google了解下。具体到Java里面的实现,有很多版本,现在不借助任何第三方jar,提供一个示例:

public static ConvolveOp getGaussianBlurFilter(intradius,
            booleanhorizontal) {
        if (radius < 1) {
            throw new IllegalArgumentException("Radius must be >= 1");
        }
        int size = radius * 2 + 1;
        float[] data = new float[size];
        float sigma = radius / 3.0f;
        float twoSigmaSquare = 2.0f * sigma *sigma;
        float sigmaRoot = (float) Math.sqrt(twoSigmaSquare *Math.PI);
        float total = 0.0f;
        for (int i = -radius; i <= radius; i++) {
            float distance = i *i;
            int index = i +radius;
            data[index] = (float) Math.exp(-distance / twoSigmaSquare) /sigmaRoot;
            total +=data[index];
        }
        for (int i = 0; i < data.length; i++) {
            data[i] /=total;
        }        
        Kernel kernel = null;
        if(horizontal) {
            kernel = new Kernel(size, 1, data);
        } else{
            kernel = new Kernel(1, size, data);
        }
        return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
    }
public static voidsimpleBlur(BufferedImage src,BufferedImage dest) {
            BufferedImageOp op = getGaussianBlurFilter(2,false);
            op.filter(src, dest);
    }

经测试模糊效果很不错。另外是图片压缩,也不借助任何第三方工具,做同质压缩。

1 public static byte[] fromBufferedImage2(BufferedImage img,String imagType) throwsIOException {
2 bos.reset();
3         //得到指定Format图片的writer
4         Iterator<ImageWriter> iter =ImageIO.getImageWritersByFormatName(imagType);
5         ImageWriter writer =(ImageWriter) iter.next(); 
6 
7         //得到指定writer的输出参数设置(ImageWriteParam )
8         ImageWriteParam iwp =writer.getDefaultWriteParam();
9         iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); //设置可否压缩
10         iwp.setCompressionQuality(1f); //设置压缩质量参数
11 
12 iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
13 
14         ColorModel colorModel =ColorModel.getRGBdefault();
15         //指定压缩时使用的色彩模式
16         iwp.setDestinationType(newjavax.imageio.ImageTypeSpecifier(colorModel,
17         colorModel.createCompatibleSampleModel(16, 16)));
18         
19 writer.setOutput(ImageIO
20 .createImageOutputStream(bos));
21         IIOImage iIamge = new IIOImage(img, null, null);
22         writer.write(null, iIamge, iwp);
23         
24         byte[] d =bos.toByteArray();
25         returnd;
26     }

至此,滑动验证码核心的代码处理流程已全部结束,这里面有很多细节可以不断打磨优化,让滑动体验可以变得更好。希望可以帮助到某些准备自己构建滑动验证码的同学。

以上代码实现都非常的精炼,一方面为了保证性能,另一方面好理解。另外由于各方面原因也不便引入过多细节,如果疑问,可留言交流。经测试,该生成滑动图形的流程响应时间可以控制在20ms左右,如果原图分辨率在300px*150px以下,可以到10ms左右,在可接受范围内。如果有更高效的方式,希望多多指教。

https://my.oschina.net/yaohonv/blog/1604185

免责声明:文章转载自《一步步实现滑动验证码(拼图验证码),Java图片处理关键代码》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇tomcat无法正常关闭问题分析及解决PLY文件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

第4步:创建RAC共享磁盘组

第4步:创建RAC共享磁盘组 方法一:使用asmdevices(推荐使用,但不适用EMC Powerpath) (1)查看硬盘的SCSI号,两个机器认到的/dev/sda对应在实际的物理盘可能不是一块,但scsi号肯定是完全一致的。 代码1 [root@sgdb1 ~]# scsi_id -g -u -d /dev/sdc 36000c294cea6...

Liunx之Centos系统无人值守全自动化安装

  作者:邓聪聪 定制centos6.8自动安装ISO光盘 安装系统为centos6.8 (base server),安装方式为全新安装 使用ext4分区格式 安装前可以交互输入root密码,主机名,分区大小,然后安装过程自动化 关闭防火墙,selinux 网络为dhcp方式获取 时区为Asia/Shanghai 分区表类型为mbr 默认设置三个分区,b...

Chelsio T520 T420安装LIO iSCSI Target Offload

安装lio_target需要特殊组件,先配置kernel,再安装lio_target   #配置kernel   rpm -ivh kernel-3.10.0-862.2.3.el7.src.rpm(忽略此次报错)   cd /root/rpmbuild/SPECS/   rpmbuild -bp kernel.spec --nodeps   cd  .....

Core Dump

什么是core dump 程序由于收到某些特定的signal之后终止了,终止过程中会产生core文件,core文件中包含了程序终止时的内存的状态,这个过程就是core dump。使用gdb工具结合可执行程序和debug symbol就能够查训到只要是类unix系统,都有这个机制。具体哪些signal会时程序产生 core dump文件,这个可以通过man...

Linux内核镜像格式【转】

转自:https://www.cnblogs.com/big-devil/p/7625880.html <Linux内核镜像格式>   Linux内核有多种格式的镜像,包括vmlinux、Image、zImage、bzImage、uImage、xipImage、bootpImage等. ➤kernel镜像格式vmlinux   vmlinux是...

下载android的linux内核的方法

1、安装git android的linux内核可以从http://android.git.kernel.org/下载,但下载需要使用git,windows版的git可以从http://code.google.com/p/msysgit/下载,有完全安装版和便携版(portable)两个版本可供选择,不常用git的话选portable版就可以了。 下载por...