You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yii2中基于场景的验证码验证失效问题排查

问题分析与解决方案

你遇到的验证码始终验证失败的问题,主要源于几个关键的代码逻辑缺陷和Yii2验证码组件的配置缺失,下面逐一拆解并给出修复方案:

1. 核心原因:缺少Captcha Action配置

Yii2的验证码组件依赖专门的控制器Action来生成验证码图片、管理会话中的验证码值。你的SiteController中没有注册这个Action,导致验证码无法正常生成和验证,这是验证码始终提示错误的最根本原因。

修复方法

在SiteController中添加captcha Action:

public function actions()
{
    return [
        'captcha' => [
            'class' => 'yii\captcha\CaptchaAction',
            'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, // 测试环境固定验证码,生产环境自动生成
        ],
    ];
}

2. 场景设置时机错误

当前代码仅在用户提交登录请求(POST)时才判断是否启用验证码场景,这会导致用户无法提前看到验证码(只有提交后才渲染带验证码的页面),进而出现"输入正确验证码仍报错"的情况——因为第一次提交的请求根本没有包含验证码参数。

修复方法

修改SiteController的actionLogin逻辑,在GET请求时就根据历史失败记录判断是否显示验证码:

public function actionLogin()
{
    if (!\Yii::$app->user->isGuest) {
        return $this->redirect(Yii::$app->getUrlManager()->getBaseUrl() . '/../../');
    }

    $model = new \common\models\LoginForm();
    $captcha = false;
    $ip = $model->get_client_ip();

    // 从session获取上次登录的用户名,判断是否需要显示验证码
    $lastUsername = Yii::$app->session->get('last_login_username');
    if ($lastUsername) {
        $captcha = $model->checkattempts($lastUsername);
    } else {
        // 无历史用户名时,检查当前IP的总失败次数
        $totalAttempts = (new \yii\db\Query())
            ->select('SUM(attempts)')
            ->from('login_attempts')
            ->where(['ip' => $ip])
            ->scalar();
        $captcha = $totalAttempts >= 3;
    }

    // 提前设置场景
    $model->scenario = $captcha ? 'withCaptcha' : 'loginpage';

    if ($model->load(Yii::$app->request->post())) {
        // 保存当前用户名到session,用于下次判断
        Yii::$app->session->set('last_login_username', $model->username);

        // 登录验证逻辑
        if ($model->validate()) {
            // 登录成功,重置该用户+IP的失败记录
            $user = \frontend\models\User::findByEmail($model->username);
            if ($user) {
                \frontend\models\LoginAttempts::deleteAll(['user_ref_id' => $user->id, 'ip' => $ip]);
            }
            Yii::$app->session->remove('last_login_username');
            return $this->goBack();
        } else {
            // 登录失败,更新失败次数记录
            $this->updateLoginAttempts($model->username, $ip);
            // 失败后强制启用验证码场景
            $model->scenario = 'withCaptcha';
            $captcha = true;
        }
    }

    return $this->render('login', [
        'model' => $model,
        'captcha' => $captcha,
    ]);
}

// 新增:更新登录失败次数的辅助方法
private function updateLoginAttempts($username, $ip)
{
    $user = \frontend\models\User::findByEmail($username);
    $userRefId = $user ? $user->id : null;

    $attempt = \frontend\models\LoginAttempts::findOne([
        'ip' => $ip,
        'user_ref_id' => $userRefId
    ]);

    if (!$attempt) {
        $attempt = new \frontend\models\LoginAttempts();
        $attempt->ip = $ip;
        $attempt->user_ref_id = $userRefId;
        $attempt->attempts = 1;
    } else {
        $attempt->attempts += 1;
    }
    $attempt->save();
}

3. checkattempts函数逻辑缺陷

原函数没有处理login_attempts记录不存在的情况(首次失败时会报错),且未覆盖"输入不存在邮箱"的失败场景。

修复方法

修改LoginForm中的checkattempts函数:

public function checkattempts($uname)
{
    $user = \frontend\models\User::findByEmail($uname);
    $ip = $this->get_client_ip();
    $userRefId = $user ? $user->id : null;

    $data = (new \yii\db\Query())
        ->select('*')
        ->from('login_attempts')
        ->where(['ip' => $ip])
        ->andWhere(['user_ref_id' => $userRefId])
        ->one();

    // 无记录则失败次数为0,否则判断是否>=3
    return $data && $data["attempts"] >= 3;
}

4. 验证码规则优化

原规则中skipOnEmpty设为true,会导致显示验证码时用户可以不输入就提交,建议改为false强制验证:

public function rules() { 
    return [ 
        [['username', 'password'], 'required', 'on'=>['loginpage', 'withCaptcha']], 
        [['reference_url'], 'safe'], 
        [['verifyCode'], 'captcha', 'skipOnEmpty' => false,'on'=>'withCaptcha'], 
        ['username','email', 'on'=>['loginpage', 'withCaptcha'], 'message'=> 'Please enter a valid email address'], 
        ['password', 'validatePassword', 'on'=>['loginpage', 'withCaptcha']], 
    ]; 
}

内容的提问来源于stack exchange,提问作者rji rji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:58:11