Yii2 使用Captcha类生成验证码

2022-09-09 16:46:51 浏览数 (1)

目录结构 如果你生成的图片验证码的代码是如下
代码语言:javascript复制
<?php
/**
 * Created by ZhengNiu.
 * User: 77103
 * Date: 2019/6/20
 * Time: 16:17
 */

namespace appcontrollers;
use yiiwebController;
use Yii;

class VerifitycodeController extends Controller
{
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yiiwebErrorAction',
            ],
            'captcha' => [
                'class' => 'yiicaptchaCaptchaAction',
                'fixedVerifyCode' => null,
                //背景颜色
                'backColor' => $this->captchaColor(1),
                //最大显示个数
                'maxLength' => 4,
                //最少显示个数
                'minLength' => 4,
                //间距
                'padding' => 2,
                //高度
                'height' => 30,
                //宽度
                'width' => 85,
                //字体颜色
                'foreColor' => $this->captchaColor(2),
                //设置字符偏移量
                'offset' => 4,
            ],

        ];
    }

    /**
     * 获取背景颜色、获取字体颜色
     *
     * @param int $type 1、获取背景颜色 2、获取字体颜色
     * @return string
     */
    protected function captchaColor($type=1)
    {
        if (!in_array($type, array(1, 2))) $type = 1;
        if ($type == 1) {
            $bg_color_arr = array('15595519', '16316664');
            $bg = $bg_color_arr[array_rand($bg_color_arr)];
            return (int)'0x' . $bg;
        } else {
            $text_color_arr = array('12326852', '2185586');
            $tc = $text_color_arr[array_rand($text_color_arr)];
            return (int)'0x' . $tc;
        }
    }

    /**
     * 默认访问
     *
     * @return string|yiiwebResponse
     */
    public function actionIndex()
    {
        $this->layout = false;
        if (Yii::$app->request->isPost) {
            $verifity = Yii::$app->request->post('verify');
            if ($this->createAction('captcha')->validate($verifity, false)) {
                return $this->asJson(['code' => 200, 'msg' => 'success']);
            } else {
                return $this->asJson(['code' => 400, 'msg' => 'fail']);
            }
        }
        return $this->render('code');
    }
}
当post 请求 index 方法 验证图片验证码时可以直接这样调用去验证。如果生成图片验证码的控制器和验证图片验证码的不是同一个控制器的话。我本来想这样去实现的,可惜没有实现,后期如果解决了,我会补充到文章里。代码如下(未实现)如果你遇到过并且解决了,请给我留言,谢谢。
代码语言:javascript复制
   /**
     * 验证 图片验证码
     *
     * @return yiiwebResponse
     */
    public function actionCheckcode1()
    {
        if (Yii::$app->request->isPost) {
            $verifity = Yii::$app->request->post('verify');
            /**
             * 第二个参数其实是Verifitycode.php控制器对象,我去看了一下框架的底层代码,但是我尝试了并未成功(可能是我理解的有问题)
             */
            $captchModel = new CaptchaAction('captcha', ?);
            if ($captchModel->validate($verifity, false)) {
                return $this->asJson(['code' => 400, 'msg' => 'fail']);
            } else {
                return $this->asJson(['code' => 200, 'msg' => 'success']);
            }
        }
    }
我感觉他的图片验证码生成的时候无非就是放到了session里面,我既然不能通过CaptchaAction类直接生成key,那我自己获取一下生成时候的key然后去session取值不就行了。我先去查找了一下验证时候的方法。拿到了code在session中存储的key(__captcha/verifitycode/captcha)就是一个前缀拼接了类和方法。验证中还有当前图片验证码的存活次数限制。于是我也对ajax验证失败时请求做了相应的处理。拿到了session中存放code的key之后, TestController.php的验证可以这样做,代码如下。
代码语言:javascript复制
    /**
     * 验证 图片验证码
     *
     * @return yiiwebResponse
     */
    public function actionCheckcode()
    {
        if (Yii::$app->request->isPost) {
            $verifity = Yii::$app->request->post('verify');
            if (Yii::$app->session['__captcha/verifitycode/captcha'] !== $verifity) {
                return $this->asJson(['code' => 400, 'msg' => 'fail']);
            } else {
                return $this->asJson(['code' => 200, 'msg' => 'success']);
            }
        }
    }
下面的这个图片时框架底层验证图片验证码的源码
解释一下源码:
代码语言:javascript复制
public function validate($input, $caseSensitive)
    {
        //获取session 中code
        $code = $this->getVerifyCode();
        //是否区分大小写验证code 是否正确
        $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
        //获取session
        $session = Yii::$app->getSession();
        $session->open();
        //获取这个code 在session 中验证的总次数 
        $name = $this->getSessionKey() . 'count';
        $session[$name]  = 1;
        //如果超过限制重新给session赋一个新的code值并且初始化这个code 已经验证的次数
        if ($valid || $session[$name] > $this->testLimit &amp;&amp; $this->testLimit > 0) {
            //重新生成
            $this->getVerifyCode(true);
        }
        return $valid;
    }
所以次数超过限制只是会改变session中code 值。页面上却没有重新改变,所以用户体验不友好。于是我页面ajax请求这样改了,代码如下。
代码语言:javascript复制
<label>验证码</label>
<span>
    <input type="text" style="width: 160px;"  id="verify">
    <img class="code-box"
         src="<?php echo yiihelpersUrl::toRoute('verifitycode/captcha'); ?>"
         alt="" id="verifyImg">
</span>
<input type="button" value="提交" id="up">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
    $(function () {
        //处理点击刷新验证码
        $("#verifyImg").on("click", function () {
            $.get("<?php echo yiihelpersUrl::toRoute('verifitycode/captcha') ?>?refresh", function (data) {
                $("#verifyImg").attr("src", data["url"]);
            }, "json");
        });
    });
    $("#up").on('click',function () {
        var input = $("#verify").val();
        if (!input) {
            alert("验证码不能为空");
            return false;
        }
        $.ajax({
            type: "POST",
            url: "<?=yiihelpersUrl::to(['test/checkcode'])?>",
            async: false,
            data: {
                'verify': input,
                '_csrf':"<?=Yii::$app->request->csrfToken ?>"
            },
            success: function (obj) {
                if (obj.code == 200) {
                    alert(obj.msg);
                } else {
                    alert(obj.msg);
                    //失败之后重新生成验证码,这样就提高了用户体验
                    $.get("<?php echo yiihelpersUrl::toRoute('verifitycode/captcha') ?>?refresh", function (data) {
                        $("#verifyImg").attr("src", data["url"]);
                    }, "json");
                    return false;
                }
            }
        });
    })
</script>
新的验证已经出炉
代码语言:javascript复制
<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2020/1/3
 * Time: 10:20
 */

namespace appcontrollers;


use apphelpershelpers;
use appmodelsLoginForm;
use Yii;
use yiifiltersAccessControl;

class TestBehaviorsController extends BaseController
{
    /**
     * 行为
     * @return array
     */
    public function behaviors()
    {
        return [
            'acf' => [
                'class' => AccessControl::className(),
                'only' => ['mine', 'index','info'],
                'rules' => [
                    [
                        'allow' => true,
                        'actions' => ['mine'],
                        'roles' => ['@'],//?未登入@登入
                    ],
                    [
                        'allow' => true,
                        'actions' => ['index'],
                        'roles' => ['?', '@'],//?未登入@登入
                    ],
                    [
                        'allow' => true,
                        'actions' => ['info'],
                        'matchCallback' => function(){
                            //return true;可以访问
                            //return false;不可以访问
                            //活动页2020/1/6 15:30-15:38可以访问
                            if (strtotime('2020-01-06 15:30:00') <= time() &&
                                strtotime('2020-01-06 15:47:00') > time()) {
                                return true;
                            }
                            return false;
                        }
                    ],
                    //其他禁止
                ]
            ]
        ];
    }

    /**
     * @return array
     */
    public function actions()
    {
        return  [
//                 'captcha' =>
//                    [
//                        'class' => 'yiicaptchaCaptchaAction',
//                        'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
//                    ],  //默认的写法
            'captcha' => [
                'class' => 'yiicaptchaCaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
                'backColor'=>0x000000,//背景颜色
                'maxLength' => 6, //最大显示个数
                'minLength' => 5,//最少显示个数
                'padding' => 5,//间距
                'height'=>40,//高度
                'width' => 130,  //宽度
                'foreColor'=>0xffffff,     //字体颜色
                'offset'=>4,        //设置字符偏移量 有效果
                //'controller'=>'login',        //拥有这个动作的controller
            ],
        ];
    }
   

    public function actionLogin()
    {

        $model = new LoginForm();
        if (Yii::$app->request->isPost) {
            $captchCode = Yii::$app->request->post('code');
            if (!$this->createAction('captcha')->validate($captchCode,false)) {
               helpers::p(111);
            }
        }
        return $this->render('login');
    }
}
代码语言:javascript复制
<?php
use yiicaptchaCaptcha;
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" action="<?= yiihelpersUrl::to(['test-behaviors/login']) ?>">
    <div> 用户名:<input type="text" name="username"></div>
    <div> 密码:<input type="text" name="password"></div>
    <div> 记住我:<input type="checkbox" name="rememberMe" value="1" checked></div>
    <div>验证码: <input type="text" autocomplete="off" placeholder="请输入验证码" name="code">
        <?php echo Captcha::widget(['name'=>'captchaimg','captchaAction'=>'/test-behaviors/captcha','imageOptions'=>['id'=>'captchaimg', 'title'=>'换一个', 'alt'=>'换一个', 'style'=>'cursor:pointer;margin-top:10px; height: 22px;'],'template'=>'{image}']); ?>
    </div>
    <input type="hidden" name="_csrf" value="<?= Yii::$app->request->csrfToken ?>">
    <div><input type="submit" name="提交"></div>
</form>
</body>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js">
</html>

0 人点赞