因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
/* @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;
?>
= Html::encode($this->title) ?>
<?php if (Yii::$app->session->hasFlash('success')): ?>
Register successful!
You can now Login.
'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' => '
]) ?>
= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
关于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;
}
(全文完)