添加和自定义CaptchaWidget

现如今在互联网上,如果你放出了一个没有做垃圾信息防护的表单,你将会在短时间内收到大量的垃圾数据。Yii有一个验证码组件,它可以让添加这样的防护非常简单。唯一的问题是没有系统的使用说明。

在接下来的例子中,我们将会给一个简单的表单添加验证码防护。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 创建一个表单模型,@app/models/EmailForm.php
  1. <?php
  2. namespace app\models;
  3. use yii\base\Model;
  4. class EmailForm extends Model
  5. {
  6. public $email;
  7. public function rules()
  8. {
  9. return [
  10. ['email', 'email']
  11. ];
  12. }
  13. }
  1. 创建控制器@app/controllers/EmailController.php
  1. <?php
  2. namespace app\controllers;
  3. use Yii;
  4. use yii\web\Controller;
  5. use app\models\EmailForm;
  6. class EmailController extends Controller
  7. {
  8. public function actionIndex(){
  9. $success = false;
  10. $model = new EmailForm();
  11. if ($model->load(Yii::$app->request->post()) && $model->validate()) {
  12. Yii::$app->session->setFlash('success', 'Success!');
  13. }
  14. return $this->render('index', [
  15. 'model' => $model,
  16. 'success' => $success,
  17. ]);
  18. }
  19. }
  1. 创建一个视图,@app/views/email/index.php
  1. <?php
  2. use yii\helpers\Html;
  3. use yii\captcha\Captcha;
  4. use yii\widgets\ActiveForm;
  5. ?>
  6. <?php if (Yii::$app->session->hasFlash('success')): ?>
  7. <div class="alert alert-success"><?=Yii::$app->session->getFlash('success')?></div>
  8. <?php else: ?>
  9. <?php $form = ActiveForm::begin()?>
  10. <div class="control-group">
  11. <div class="controls">
  12. <?= $form->field($model, 'email')->textInput(['class' => 'form-control']); ?>
  13. <?php echo Html::error($model, 'email', ['class' => 'help-block'])?>
  14. </div>
  15. </div>
  16. <?php if (Captcha::checkRequirements() &&
  17. Yii::$app->user->isGuest): ?>
  18. <div class="control-group">
  19. <?= $form->field($model, 'verifyCode')-widget(\yii\captcha\Captcha::classname(), [
  20. 'captchaAction' => 'email/captcha'
  21. ]) ?>
  22. </div>
  23. <?php endif; ?>
  24. <div class="control-group">
  25. <label class="control-label" for=""></label>
  26. <div class="controls">
  27. <?=Html::submitButton('Submit', ['class' => 'btn btn-success'])?>
  28. </div>
  29. </div>
  30. <?php ActiveForm::end()?>
  31. <?php endif;?>
  1. 现在,我们有了一个电子邮件提交表单,如下截图所示,它验证了电子邮件字段。让我们添加验证码:

添加和自定义CaptchaWidget - 图1

如何做…

  1. 首先我们需要自定义表单模型。我们需要添加$verifyCode,它会保存输入的验证码,并为它添加一个验证规则:
  1. <?php
  2. namespace app\models;
  3. use yii\base\Model;
  4. use yii\captcha\Captcha;
  5. class EmailForm extends Model
  6. {
  7. public $email;
  8. public $verifyCode;
  9. public function rules()
  10. {
  11. return [
  12. ['email', 'email'],
  13. ['verifyCode', 'captcha', 'skipOnEmpty' => !Captcha::checkRequirements(), 'captchaAction' => 'email/captcha']
  14. ];
  15. }
  16. }
  1. 然后添加一个外部动作到控制器中:
  1. public function actions()
  2. {
  3. return [
  4. 'captcha' => [
  5. 'class' => 'yii\captcha\CaptchaAction',
  6. ],
  7. ];
  8. }
  1. 在视图中,我们需要展示一个额外的字段和验证码图片:
  1. ...
  2. <?php if (Captcha::checkRequirements() &&
  3. Yii::$app->user->isGuest): ?>
  4. <div class="control-group">
  5. <?=Captcha::widget([
  6. 'model' => $model,
  7. 'attribute' => 'verifyCode',
  8. ]);?>
  9. <?php echo Html::error($model, 'verifyCode')?>
  10. </div>
  11. <?php endif; ?>
  12. ...
  1. 同时,不要忘记在视图的头部添加Captcha导入:
  1. <?php
  2. use yii\helpers\Html;
  3. use yii\captcha\Captcha;
  4. ?>
  1. 完成了。现在你可以运行电子邮件控制器,可以在动作动看到验证码,如下截图所示:

添加和自定义CaptchaWidget - 图2

如果屏幕上没有错误,表单中没有Captcha字段,很有可能是因为你没有安装PHP扩展GD或者Imagick。验证码依赖于GD或者Imagick生成图片。我们添加了几个Captcha::checkRequirement()检查,所以当图片不会展示时,不使用验证码,应用仍可以正常工作。

工作原理…

在视图中,我们调用验证码小部件渲染img标签,将src属性指向控制器中的验证码动作。在这个动作中,生成了一张带有随机单词的图片。生成的单词需要用户输入到表单中。它被存储在一个用户session中,并向用户展示了一张图片。党用户输入电子邮箱和验证码到表单中时,我们将这些值赋给表单模型,并进行校验。对于验证码的校验,我们使用CaptchaValidator。它会从用户session获取验证码,并和输入的验证码进行比较。如果不匹配,模型数据会被认为是不合法的。

更多…

如果你使用accessRules控制器方法来限制对控制器动作的访问,不要忘记授权每一个人都能访问他们:

  1. public function behaviors()
  2. {
  3. return [
  4. 'access' => [
  5. 'class' => AccessControl::className(),
  6. 'rules' => [
  7. [
  8. 'actions' => ['index', 'captcha'],
  9. 'allow' => true,
  10. ]
  11. ],
  12. ],
  13. ];
  14. }