今许多的网站都用到了滑动拼图的验证方式,因为它操作非常简单,只需要用户点击鼠标拉动,就能完成验证。减少了用户的输入,比较新颖,大大提高了用户体验的同时,安全性也没有降低。




Android滑动拼图验证码控件使用方法详解 滑动拼图验证身份原理_二维数组


主要类库:PHP的GD库

核心流程:后端生成一张从原图中随机位置抠出的拼图状的图与带有水印的原图,记录抠图坐标。前端拿到图片后,实现滑动交互,获取到用户拖动拼图的位移值,传给后端,后端校验误差是否在合理范围之内。

具体步骤

一、确定拼图的轮廓

如何从一副图中抠出拼图形状呢?图片,都是由一个个像素组成。一个像素一个颜色,组成一幅图。在GD库里,一张图片的原点在图片的左上角,往右是X轴,往下是y轴。如此(x,y)就是一个像素。因此,一副图的所有像素可以形成一个二维数组,类似$arr[y][x]。只要我们判断知道某个坐标在我们想要得到的轮廓里面,就设为$arr[y][x] = 1,反之,设为0,我们就能得到一个拼图轮廓的二维数组。

一个拼图可以理解为有一个矩形和若干个圆形组成 ,矩形简单,只要判断该坐标x和y是否在某个特定范围内就行了。
那圆形呢?圆形我们必须要用到圆形的公式,(x-a)²+(y-b)²=r²,x和y就是我们的坐标,而a和b就是圆中心的坐标,r就是圆的半径,那么,我们根据公式就不难知道某坐标是否在圆内。

示例代码如下


$pic_w = 100;
        $pic_h = 100;
        $r = 10;    //半径
        $dr = $r*$r;    //半径平方
        $local_w = $r*2-5;
        //第一个圆中心点
        $circular1_x = $local_w+($pic_w-($local_w)*2)/2;
        $circular1_y = $r;
        //第二个圆中心点
        $circular2_x = $pic_h-$r;
        $circular2_y = $local_w+($pic_h-($local_w)*2)/2;;

        $jigsawArray = array();
        for($i=0;$i<$pic_h;$i++){
            for($j=0;$j<$pic_w;$j++){
                //根据公式(x-a)²+(y-b)² = r² 算出像素是否在圆内
                $d1 = pow($j-$circular1_x,2)+pow($i-$circular1_y,2);
                $d2 = pow($j-$circular2_x,2)+pow($i-$circular2_y,2);

                if(($i>=$local_w && $j>=$local_w && $i<=$pic_h-$local_w && $j<=$pic_w-$local_w) || $d1<=$dr || $d2<=$dr){
                    $jigsawArray[$i][$j] = 1;   //在拼图内为1
                } else {
                    $jigsawArray[$i][$j] = 0;   //在拼图外为0
                }
            }
        }


二、从原图随机位置抠图并打上水印

在上一步我们得到了一个拼图轮廓(二维数组),根据这个二维数组的值,我们就能在原图中抠出一个拼图形状出来,并打上水印。

示例代码如下


//原图
        $srcFile = $this->srcPic;
        $src_w   = $this->pic_info['w'];    //原图宽
        $src_h   = $this->pic_info['h'];    //原图高
        $src_im  = imagecreatefrompng($srcFile);


        $dst_im = imagecreatetruecolor($src_w,$src_h);
        //根据原图尺寸创建一个相同大小的真彩色位图
        //给新图填充背景色
        $black = imagecolorallocate($dst_im,0,0,0);//黑
        imagefill($dst_im,0,0,$black);
        imagecopymerge($dst_im,$src_im,0,0,0,0,$src_w,$src_h,100);//原图图像写入新建真彩位图中

        //生成透明底拼图画布
        $jigsaw_im = imagecreatetruecolor($pic_w,$pic_h);   //100*100拼图画布
        imagealphablending($jigsaw_im,false);
        imagesavealpha($jigsaw_im, true);
        $jigsaw_bg = imagecolorallocatealpha($jigsaw_im,0,0,0,127);
        imagefill($jigsaw_im, 0, 0, $jigsaw_bg);

        //随机位置
        $src_x = mt_rand(215,$src_w-$pic_w);; //水印位于大图x坐标
        $src_y= mt_rand(0,$src_h-35);; //y坐标

        $rgb_white = imagecolorallocatealpha($dst_im,127,127,127,60);   //灰色水印
        //循环轮廓抠图
        for($i=0;$i<$pic_h;$i++){
            for ($j=0;$j<$pic_w;$j++){
                if($this->jigsawArray[$i][$j] == 1){
                    //如果在轮廓内,取原图像素颜色
                    $rgb = ImageColorAt($dst_im,$src_x+$j,$src_y+$i);
                    imagesetpixel($jigsaw_im,$j,$i,$rgb);   //在拼图画布上色
                    //在原图挖坑(打上灰色)
                    imagesetpixel($dst_im,$src_x+$j,$src_y+$i,$rgb_white);
                }
            }
        }

        //保存图片
        imagepng($dst_im,'big.png');
        imagepng($jigsaw_im,'jigsaw.png');
        imagedestroy($src_im);
        imagedestroy($dst_im);
        imagedestroy($jigsaw_im);


这样,我们就得到了我们想要的拼图和有水印的原图啦。只要我们将这两张图和随机位置的y值给到前端,由前端进行交互,拿到用户拖动位置值给后端就行了。

三、校验

校验非常简单,无非就是判断用户拖动的值跟随机位置的x值是否在一个合理的范围内。so easy!


/**
     * @nmae 校验数据合法性
     * @param int $val  用户拉动拼图偏移量
     * @param int $rules 标准位(x)
     * @return bool
     */
    public functioncheckData($val,$rule)
    {
        if( empty($val) || !is_numeric($val)  ||  empty($rule) || !is_numeric($rule) ) {
            return false;
        }
        //允许偏移量
        $max = $rule+6;
        $min = $rule-6;
        if( $val <= $max &&  $val >= $min  ) {
            return true;
        } else {
            return false;
        }
    }


以上,滑动验证码的基本核心处理步骤以及结束。这里面还有很多细节需要各位自行调节,比如轮廓的大小,比如如何让拼图和水印更好看更立体之类的,在此就不多说了。