在Yii2框架中,用户身份认证并不是简单写一条SQL查账号密码,而是一套由组件、接口和会话共同约束的机制。理解这套机制,才能在不引入安全漏洞的前提下完成登录功能。

一、IdentityInterface接口的实现
Yii2要求代表用户的类实现yiiwebIdentityInterface。该接口定义了框架获取用户标识、根据ID找回用户、以及校验状态的方法。通常我们会让AR模型(如User)直接实现它,这样既能操作数据库,又能被User组件调用。
下面给出一个最小但安全的实现示例。注意validatePassword并非接口强制方法,但它是Yii2官方模板约定的密码校验入口,必须在模型里写好,且使用password_hash与password_verify而不是明文比较。
<?php
namespace appmodels;
use yiidbActiveRecord;
use yiiwebIdentityInterface;
class User extends ActiveRecord implements IdentityInterface
{
// 根据ID返回用户实例
public static function findIdentity($id)
{
return static::findOne($id);
}
// 根据令牌返回用户,本例不使用令牌登录可返回null
public static function findIdentityByAccessToken($token, $type = null)
{
return null;
}
// 返回用户主键
public function getId()
{
return $this->id;
}
// 返回auth_key,用于自动登录校验
public function getAuthKey()
{
return $this->auth_key;
}
// 校验auth_key
public function validateAuthKey($authKey)
{
return $this->auth_key === $authKey;
}
// 密码校验,使用PHP原生函数
public function validatePassword($password)
{
return password_verify($password, $this->password_hash);
}
// 注册或改密码时生成哈希
public function setPassword($password)
{
$this->password_hash = password_hash($password, PASSWORD_DEFAULT);
}
}
上述代码中,password_hash每次生成的摘要都带随机盐,因此数据库里绝不该出现明文密码列。若旧系统使用MD5或SHA1单向加密,应借登录成功时机迁移到password_hash。
实现接口后,还需要在配置文件中把user.identityClass指向该类,否则User组件不知道去哪找用户。配置错误会导致Yii::$app->user->identity一直为空。
二、登录控制器与User组件协作
真正的登录动作发生在SiteController或独立AuthController里。Yii2的Yii::$app->user->login()方法会调用Identity的接口方法,并把会话写入到服务端或客户端Cookie中。
以下示例展示标准登录流程:先根据用户名取记录,再调用模型的validatePassword,最后把用户对象交给User组件。不要在控制器里直接写SQL比对,那样既重复造轮子又容易漏掉状态封禁检查。
<?php
namespace appcontrollers;
use Yii;
use yiiwebController;
use appmodelsUser;
use appmodelsLoginForm;
class AuthController extends Controller
{
public function actionLogin()
{
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$user = User::findOne(['username' => $model->username]);
if ($user && $user->validatePassword($model->password)) {
// 第二个参数为记住登录的秒数
Yii::$app->user->login($user, 3600 * 24 * 7);
return $this->goHome();
}
$model->addError('password', '账号或密码错误');
}
return $this->render('login', ['model' => $model]);
}
}
这里把登录表单与用户模型分离,LoginForm只负责校验输入格式,用户模型负责凭证比对,职责清晰也方便单元测试。若用户被封禁,应在validatePassword之前检查status字段,避免已禁用账号仍可登录。
User组件默认使用session保持登录态,若开启enableAutoLogin,则会把auth_key写入Cookie实现下次自动登录。该Cookie应设为httpOnly并走HTTPS,防止前端脚本窃取。
三、会话安全与防暴破措施
身份认证做完了,并不等于安全。攻击者可以暴力尝试密码,或在公共电脑借用未注销的会话。Yii2提供了几项基础配置来缓解风险。
首先,在全局配置里限制登录失败次数。虽然框架没有内置限流,但可借助RateLimiter行为或简单IP计数。其次,注销时必须调用Yii::$app->user->logout()销毁会话,而不能只前端跳走。最后,会话Cookie参数应统一在components.session中设置。
<?php
// 在config/web.php的components里
'session' => [
'cookieParams' => [
'httponly' => true,
'secure' => true, // 仅HTTPS传输
],
],
'user' => [
'identityClass' => 'appmodelsUser',
'enableAutoLogin' => true,
'authTimeout' => 900, // 15分钟无操作失效
],
设置authTimeout能确保后台用户离开后凭证自动过期,比单纯依赖浏览器关闭更可靠。对于管理后台,还可将absoluteAuthTimeout设更小值,强制周期性重新认证。
另外,不要相信客户端传来的用户ID。所有权限判断必须基于Yii::$app->user->id而非表单隐藏域,否则会出现越权修改他人数据的问题。CSRF验证也应保持开启,防止跨站提交登录请求。
四、常见错误与修复对照
开发中经常看到几种危险写法。第一种是把密码用MD5加固定盐存库,且登录时用md5($pwd.$salt)比对,这已被彩虹表轻易破解。第二种是在控制器直接User::find()->where(['name'=>$_POST['name'],'pwd'=>$_POST['pwd']])->one(),等于把明文密码送进数据库查询。第三种是login()后不检查返回值,以为调用即成功。
对应修复方式是统一走password_hash、用模型方法校验、判断login返回布尔值。下面用表格列出差异:
| 错误做法 | 风险 | 正确做法 |
|---|---|---|
| 明文或弱哈希存密码 | 数据库泄露即全裸 | password_hash加随机盐 |
| 控制器内拼SQL查密码 | 逻辑分散难维护 | 模型validatePassword校验 |
| 不校验login返回值 | 登录失败仍进首页 | 判断布尔并提示错误 |
把认证逻辑收敛到模型与User组件,不仅符合Yii2设计哲学,也便于后续接入RBAC权限控制。当业务需要扩展手机验证码或OAuth第三方登录时,只要新增Identity实现或改写findIdentityByAccessToken即可,核心会话机制无需变动。
总体来看,Yii2的用户身份认证是一套低耦合、可替换的实现。抓住IdentityInterface与User组件两个支点,配合安全的密码存储和会话配置,就能搭建出经得起审查的登录系统。
Yii2用户身份认证UserIdentity修改时间:2026-08-09 16:48:35