3d(三维)验证码实现(部分代码来自网络),旋转未完成,有待改进(一)

本文深入探讨了一种基于PHP的3D验证码生成算法,包括其核心组件、工作原理及实现细节,旨在提供一种有效防止自动化攻击的解决方案。

<?php
class captcha {
    static private $_str = 'aAbBdDeEfFgGhHiJLmMnNqQrRtTyY23456789';//只允许的字符串

    /*
     * 清空验证码
     */
    static public function clear() {
        !session_id() && session_start();
        unset($_SESSION['qidiziCaptcha']);
    }
    /*     * 验证输入     * return 正确:true,错误:false,其它:false
     */
    static public function verify($input = null) {
        !session_id() && session_start();

        if (empty($input) || empty($_SESSION['qidiziCaptcha'])) {
            return false;
        }

        $input = (strtolower($input) === $_SESSION['qidiziCaptcha']);
        captcha::clear(); //防止只请求一次输出验证码,而多次请求验证的漏洞.
        return $input;
    }
    /*     * 输出图片     * return 验证码字符串     */
    static public function init() {
        !session_id() && session_start();        
        $minCount = 4;//码最少个数
        $maxCount = 5;//码最多个数
        $captchaText = captcha::_rmBadWord(rand($minCount, $maxCount));//获取随机字符        
        $_SESSION['qidiziCaptcha'] = strtolower($captchaText);
        $rndM = 10;
        $rnd = rand(-$rndM, $rndM);
        $scale3d = 1.3 + abs($rnd / $rndM);//3d背景的比率
        
        // Calculate projection matrix
        $T = captcha::_cameraTransform(//三维图像的向量
                array(//方向
                    $rnd//左右旋转角度,随机之间
                    ,-40 //前后旋转角度,负数为从前向后转(正字),正数从后向前转(倒字)
                    ,50 //3d凸起感,跟前面二个参数相互作用.针对于看得清的原则,前后角度可以达到凸起角度二倍,但是左右角度就很小了.
                )
                ,array(//标量
                    0 //3d投影离左边距,正数左移,负数右移
                    , 0 //3d投影离顶边距,正数上移,负数下移
                    , 0 //3d投影z轴投影深度,正数上移,负数下移
                )
            );
        $T = captcha::_matrixProduct(
                $T,
                captcha::_viewingTransform(
                    80 //缩放,不能写0,负数反转,
                    , 300//二参数不明,后者必须大于前者
                    , 301
                )
            );

        // Calculate coordinates
        $coord = array();
        $count = 0;
        //echo $captchaText;
        $chars = captcha::_getCharCode($captchaText);
        //exit(show2d(turnLR($chars, 10)));
        //show2d($chars);exit;

        if (empty($chars)) {
            exit('empty captcha text.');
        }

        $width = count($chars[0]);//坚向是宽
        $height = count($chars);//横向是高

        for ($y = 0; $y < $height; $y += 1) {//2d点变3d点,调整$y+=2剃度将改变上下线条密码
            for ($x = 0; $x < $width; $x++) {
                $xc = $x - $width / 2;//2d图片x,左上角是0,0
                $yc = -$chars[$y][$x];
                $zc = $y - $height  / 2;
                $xyz = array($xc, $yc, $zc, 1);
                $xyz = captcha::_vectorProduct($xyz, $T);
                $coord[$count] = $xyz;
                $count++;
            }
        }
        
        $scale = 3;//3d字与2d字缩放,最好在1到2之间
        $image3d_x = $width * $scale * 1.3;//外背景横向边长,上面的绽放是60时,几乎是1.75关系
        $image3d_y = $height * $scale * $scale3d;//外背景高度
        $image3d = imagecreatetruecolor($image3d_x, $image3d_y);//创建外背景区域大小
        $bgcolor = imagecolorallocate($image3d, 237, 237, 237);
        //$txtcolor = imagecolorallocate($image3d, 0, 220, 0);
        imagefill($image3d, 0, 0, $bgcolor);//填充外背景
        //imagestring($image3d, 5, 5, 2, 'http://chinahrd.net', $txtcolor);//输入水印
        $count = 0;
        $core3dX = $image3d_x / 2;//3d背景中心xy
        $core3dY = $image3d_y / 2;
        $lineSpan = 1.3;//三d上下二线间隔
        $fgcolor = imagecolorallocate($image3d, 0, 0, 0);//随机线笔色
        
        for ($y = 0; $y < $height; $y++) {
            for ($x = 0; $x < $width; $x++) {
                if ($x > 0) {
                    $x0 = $core3dX + $coord[$count - 1][0] * $scale;//原坐标加上背景中心坐标可以让3d显示居中,*以倍数可以放大.
                    $y0 = $core3dY + $coord[$count - 1][1] * $scale * $lineSpan;
                    $x1 = $core3dX + $coord[$count][0] * $scale ;
                    $y1 = $core3dY + $coord[$count][1] * $scale  * $lineSpan;
                    imageline($image3d, $x0, $y0, $x1, $y1, $fgcolor);//在点0 到 点1之间画线
                }
                $count++;
            }
        }
        header("Content-type: image/jpeg", true);
        header("Cache-Control: no-cache, must-revalidate", true); // HTTP/1.1
        header("Expires: Mon, 03 Apr 1977 11:05:00 GMT", true); // 立刻过期,不缓存
        imagejpeg($image3d);
        return $captchaText;
    }
    //生成一个三维点坐标
    static private function _addVector($a, $b) {
        return array($a[0] + $b[0], $a[1] + $b[1], $a[2] + $b[2]);
    }
    //二个数乘积:针对三维点改变量,方向不变
    static private function _scalarProduct($vector, $scalar) {
        return array($vector[0] * $scalar, $vector[1] * $scalar, $vector[2] * $scalar);
    }
    //三维点积,得到标量:计算机图形学常用来进行方向性判断,如两矢量点积大于0,则它们的方向朝向相近;如果小于0,则方向相反
    static private function _dotProduct($a, $b) {
        return ($a[0] * $b[0] + $a[1] * $b[1] + $a[2] * $b[2]);
    }
    //对某三维点缩小为:点积平方根分之一
    static private function _normalize($vector) {
        return captcha::_scalarProduct($vector, 1 / sqrt(captcha::_dotProduct($vector, $vector))/*点积平方根分之一*/  );
    }
    //叉积:两个向量的叉积与这两个向量都垂直,最终形成一个立方体,如平台二线积形成长方形一样.
    static private function _crossProduct($a, $b) {
        return array(
            ($a[1] * $b[2] - $a[2] * $b[1]),
            ($a[2] * $b[0] - $a[0] * $b[2]),
            ($a[0] * $b[1] - $a[1] * $b[0])
            );
    }
    //计算出3d外围二点:投影参数+平面点
    static private function _vectorProductIndexed($xyz, $m, $i) {
        return array(
            $xyz[$i + 0] * $m[0] + $xyz[$i + 1] * $m[4] + $xyz[$i + 2] * $m[8] + $xyz[$i + 3] * $m[12],
            $xyz[$i + 0] * $m[1] + $xyz[$i + 1] * $m[5] + $xyz[$i + 2] * $m[9] + $xyz[$i + 3] * $m[13],
            $xyz[$i + 0] * $m[2] + $xyz[$i + 1] * $m[6] + $xyz[$i + 2] * $m[10]+ $xyz[$i + 3] * $m[14],
            $xyz[$i + 0] * $m[3] + $xyz[$i + 1] * $m[7] + $xyz[$i + 2] * $m[11]+ $xyz[$i + 3] * $m[15]
            );
    }
    //对字点(笔划1/背景0=y,背景width点=x,背景height点=z)应用投影的参数);
    static private function _vectorProduct($xyz, $mv) {
        return captcha::_vectorProductIndexed($xyz, $mv, 0);
    }
    //矩阵积:相当于把3d点附加有xyz/绽放属性
    static private function _matrixProduct($a, $b) {
        $o1 = captcha::_vectorProductIndexed($a, $b, 0);
        $o2 = captcha::_vectorProductIndexed($a, $b, 4);
        $o3 = captcha::_vectorProductIndexed($a, $b, 8);
        $o4 = captcha::_vectorProductIndexed($a, $b, 12);

        return array(
            $o1[0], $o1[1], $o1[2], $o1[3],
            $o2[0], $o2[1], $o2[2], $o2[3],
            $o3[0], $o3[1], $o3[2], $o3[3],
            $o4[0], $o4[1], $o4[2], $o4[3]
            );
    }
    //首先来介绍三个基本的矩阵,这是direct 3d中必须设置的三个矩阵,即世界矩阵,视图矩阵,投影矩阵。而对应的三个变换就是世界变换(World Transform),摄像机变换(Camera Transform),和投影变换(Projection Transform)
    //摄像机转换
    //应该是使用了斜平面投影
    // http://graphics.idav.ucdavis.edu/education/GraphicsNotes/Camera-Transform/Camera-Transform.html
    static private function _cameraTransform($C, $A) {
        $w = captcha::_normalize(captcha::_addVector($C, captcha::_scalarProduct($A, -1)));//世界坐标点
        $y = array(0, 1, 0);
        $u = captcha::_normalize(captcha::_crossProduct($y, $w));//用户坐标点
        $v = captcha::_crossProduct($w, $u);
        $t = captcha::_scalarProduct($C, -1);//投影坐标点

        return array(
            $u[0], $v[0], $w[0], 0,
            $u[1], $v[1], $w[1], 0,
            $u[2], $v[2], $w[2], 0,
            captcha::_dotProduct($u, $t), captcha::_dotProduct($v, $t), captcha::_dotProduct($w, $t), 1
            );
    }
    //三维观察变换
    // http://graphics.idav.ucdavis.edu/education/GraphicsNotes/Viewing-Transformation/Viewing-Transformation.html
    static private function _viewingTransform($fov, $n, $f) {
        $fov *= (M_PI / 180);
        $cot = 1 / tan($fov / 2);

        return array(
            $cot,    0,        0,        0,
            0,        $cot,    0,        0,
            0,        0,        ($f + $n) / ($f - $n),        -1,
            0,        0,        2 * $f * $n / ($f - $n),    0
            );
    }
    /*     * 随机产生字符     */
    static private function _rndStr($length) {
        $n = strlen(captcha::$_str);
        $captchaText = '';

        for ($i = 0; $i < $length; $i++) {
            $captchaText .= captcha::$_str[rand(0, $n - 1)];
        }

        return $captchaText;
    }
    /*
     * 移除不文明英文
     */
    static private function _rmBadWord($length) {
        $badWords = array(
             'anal'
            ,'whore'
        );
        do {
            $cleanText = captcha::_rndStr($length);
        } while (in_array($cleanText, $badWords));

        return $cleanText;
    }
    /*
     * 插算拉宽
     */
    static private function _doWidths($font, $times) {
        if ($times < 1) {
            return $font;
        }

        foreach ($font as $rk => $row) {
            $tmp = array();

            foreach ($row as $cell) {
                $tmp = array_pad($tmp, count($tmp) + $times, $cell);//复制列点n次
            }

            $font[$rk] = $tmp;
        }

        return $font;
    }
    /*
     * 插算拉高
     */
    static private function _doHeights($font, $times) {
        if ($times < 1) {
            return $font;
        }

        $tmp = array();

        foreach ($font as $row) {
            $tmp = array_pad($tmp, count($tmp) + $times, $row);//行复制n次
        }

        return $tmp;
    }

 

---代码未完-----

3d(三维)验证码实现(部分代码来自网络),旋转未完成,有待改进(一) - qidizi - qidizi 的博客

--------------使用了预点阵码,去掉写字再像素提取步骤,大部分是3维知识,资料过少,网上有直接是代码形态的知识点,中文详细次数比较少,难理解,3d坐标计算比较难掌握----

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值