Yii2验证码Captcha使用Ajax无法验证的处理方法 [ 技术分享 ]
因Yii2验证码Captcha原校验方式有bug,总是报错:验证码不正确,导致无法实现Ajax验证,今天研究了一下Yii2的源代码,把这个问题解决掉了。
主要解决方法:
1)还是使用Yii2的yii\captcha\CaptchaAction,进行验证码的生成、显示等操作。 2)但使用自定义的codeVerify验证方法进行验证,避开Yii2的验证Bug 3)验证完成后要重新生成新的验证码。 有其他特殊需求可以在这些代码中自己添加即可。
源代码:
文件位置:D:\phpwork\news\models\RegisterForm.php
class RegisterForm extends Model{
public function rules()
{
return [
//替代['verifyCode', 'captcha'],
['verifyCode', 'codeVerify'],
......
];
}
//使用自定义的codeVerify验证方法进行验证,避开Yii2的验证Bug
public function codeVerify($attribute) {
//参数:'captcha',即控制器中actions()内的名称'captcha';Yii::$app->controller,调用验证的当前控制器(必须设置)
$captcha_validate = new \yii\captcha\CaptchaAction('captcha',Yii::$app->controller);
if($this->$attribute){
$code = $captcha_validate->getVerifyCode();
if($this->$attribute!=$code){
$this->addError($attribute, 'The verification code is incorrect.');
}
}
}
文件位置:D:\phpwork\news\controllers\SiteController.php
class SiteController extends CommonController{
public function actions(){
return [
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
],
];
}
public function actionRegister()
{
$model = new RegisterForm();
if ($model->load(Yii::$app->request->post())) {
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return \yii\widgets\ActiveForm::validate($model);
}
if($model->validate()){
$error=$model->save();
if($error) {
return $this->errorDisplay($error);
}else{
//数据存储成功后,要重新生成新的验证码,否则原验证码不会改变
$captcha_validate = new \yii\captcha\CaptchaAction('captcha',$this);
$captcha_validate->getVerifyCode(true);
Yii::$app->session->setFlash('success');
return $this->refresh();
}
}else{
return $this->errorDisplay($model->getErrors());
}
}else{
return $this->render('register', [
'model' => $model,
]);
}
}
文件位置:D:\phpwork\news\views\site\register.php
<?php
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\ContactForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
$this->title = 'User Register';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-contact">
<h1><?= Html::encode($this->title) ?></h1>
<?php if (Yii::$app->session->hasFlash('success')): ?>
<div class="alert alert-success">
Register successful!
</div>
<p>
You can now <a href="/site/login">Login</a>.
</p>
<?php else: ?>
<div class="row">
<div class="col-lg-6">
<?php $form = ActiveForm::begin([
'id' => 'contact-form',
]); ?>
<?= $form->field($model, 'member',['enableAjaxValidation'=>true])->textInput(['autofocus' => true])->hint('Can be chinese,grapheme or number.Only use to login,not public display!') ?>
<?= $form->field($model, 'memkey')->passwordInput() ?>
<?= $form->field($model, 'memkey_repeat')->passwordInput() ?>
<?= $form->field($model, 'nickname',['enableAjaxValidation'=>true])->textInput()->hint('Can be chinese,grapheme or number,can have blank space.For public display only!') ?>
<?= $form->field($model, 'verifyCode',['enableAjaxValidation'=>true])->widget(Captcha::className(), [
'captchaAction'=>'site/captcha',
'imageOptions'=>['id'=>'captchaimg','alt'=>'点击换图','title'=>'点击换图', 'style'=>'cursor:pointer'],
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>
<div class="form-group">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php endif; ?>
</div>
关于Yii2图形验证码Bug产生的原因
Yii2图形验证码Bug产生的原因:首次验证通过后会重新生成验证码,导致二次验证失败! 具体执行过程:方法validate($input, $caseSensitive)在Ajax进行第一次验证后,$valid为true,导致$this->getVerifyCode(true);被执行,即重新生成了新的VerifyCode,当$model->verify()进行第二次验证时,则必定失败(新的VerifyCode与刚才输入的已经不一样了!),以下是Yii2原码:
文件位置:D:\phpwork\news\vendor\yiisoft\yii2\captcha\CaptchaAction.php
public function validate($input, $caseSensitive)
{
$code = $this->getVerifyCode();
$valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
$session = Yii::$app->getSession();
$session->open();
$name = $this->getSessionKey() . 'count';
$session[$name] = $session[$name] + 1;
//Bug由以下三行代码产生:
if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {
$this->getVerifyCode(true);
}
return $valid;
}
(全文完)
共 8 条回复
-
Lan_allofme 回复于 2017-05-11 15:58 举报
我也踩到这个坑了,谢up主分享经验
-
感谢楼主,特地登录回复,表示感谢! 最好转换成小写,不然还是验证失败。
public function codeVerify($attribute) {$captcha_validate = new \yii\captcha\CaptchaAction('captcha',Yii::$app->controller); if($this->$attribute){ $code = $captcha_validate->getVerifyCode(); $verify=$this->$attribute; if(strtolower($verify)==strtolower($code)){ return true; }else{ $this->addError($attribute, 'The verification code is incorrect.'); } } } } }
-
继承yii的CaptchaAction,将这个validate方法重写一下,去掉验证成功重新获取验证码的那个条件,然后在你使用captcha的控制器中将captcha的class改为这个新建的CaptchaAction应该就可以了
public function actions() { return [ 'captcha' => [ **'class' => 'common\actions\CaptchaAction',** 'minLength' => 4, 'maxLength' => 4, 'transparent' => true, 'offset' => 8, 'padding' => 1, ], ]; }
共 1 条回复 -
public function prevCaptcha($attribute, $params) { $captcha = $this->createCaptchaAction(); $value = $this->vcode; $valid = !is_array($value) && $captcha->validate($value, $this->caseSensitive); if (!$valid) { $this->addError($attribute, ErrorCode::ERROR_VCODE_NOT_MATCH); } } public function createCaptchaAction() { $ca = Yii::$app->createController($this->captchaAction); return $this->captchaAction; if ($ca !== false) { /* @var $controller \yii\base\Controller */ list($controller, $actionID) = $ca; $action = $controller->createAction($actionID); $action->regenerateAfterValidation = false; if ($action !== null) { return $action; } } throw new InvalidConfigException('Invalid CAPTCHA action ID: ' . $this->captchaAction); }
上面是重写的验证方法,总在下面这一步报错,有什么解决办法吗
$ca = Yii::$app->createController($this->captchaAction);
$this->captchaAction = 'api/article/captcha';共 3 条回复皎然 觉得很赞
阿江
最后登录:2024-03-03
在线时长:186小时21分
- 粉丝94
- 金钱16816
- 威望160
- 积分20276