1、背景
Yii2提供了默认的登录机制,今天我们来详细解读一下源码,看一下它是怎么工作的。
2、登录路由
2.1 登录页面
我们通过url可以找到登录的控制器入口,url如下:
http://172.22.0.3/index.php?r=site%2Flogin
因此入口应该是backend/controllers/SiteController.php
的actionLogin()
方法:
public function actionLogin()
{
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$this->layout = 'blank';
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
} else {
$model->password = '';
return $this->render('login', [
'model' => $model,
]);
}
}
通过访问这个方法,用$this->render
实现MVC模式,渲染页面,从而浏览器展示登录页面,从代码中可以看到,view页面所在位置是:backend/views/site/login.php
。
2.2 登录请求
在登录页面填好账号密码后,点击登录按钮,请求到哪里去呢?从页面的元素中我们可以看到,其实就是把请求投递到当前页面所在的控制器,因为action
的值和当前页面的url
是一致的:
<form id="login-form" action="/index.php?r=site%2Flogin" method="post">
3、LoginForm
common/models/LoginForm.php
是对登录验证的主要模型,这个模型继承于yii\base\Model
,因此可以使用Yii提供的数据验证机制来实现数据有效性验证。
3.1 load()
$model->load(Yii::$app->request->post())
这个方法会把请求过来的数据填充到LoginForm->attributes
中,也就是对类属性赋值,其实load($data, $formName = null)
还有第二个参数,用于指定$formName
,为啥要这个属性呢?因为post过来的数据有多个层次,如下:
array(3) {
["_csrf-backend"]=>
string(88) "BGsAOWgj1SMXlli3QpPFaZClyaac9_9gErgS9Bk_Vw88DWtsOGyWG3LkHM0l9qscvcT45d6dpRVFiV2SWn0iSg=="
["LoginForm"]=>
array(3) {
["username"]=>
string(2) "ad"
["password"]=>
string(6) "123456"
["rememberMe"]=>
string(1) "0"
}
["login-button"]=>
string(0) ""
}
yii需要知道$formName = "LoginForm"
才能从post中的数据获取属性值。但是上述代码中并没有指定$formName
,从源代码可知,如果$formName
不设置,会取当前类名称作为它的值。
3.2 login()
<?php
namespace common\models;
... ...
class LoginForm extends Model
{
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
}
return false;
}
... ...
}
此处正是验证用户名和密码的核心代码,由于LoginForm()
继承于yii\base\Model
,因此可以使用验证器,代码通过$this->validate()
进行验证数据有效性,验证规则如下:
<?php
namespace common\models;
... ...
class LoginForm extends Model
{
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
... ...
}
通过上述规则可以实现如下功能:
username
和password
是必须要有的属性,就是说点击登录按钮后必须要传进来的参数,否则的话会报如下错误:
而password
还有一条验证规则,就是通过validatePassword
方法进行验证:
<?php
namespace common\models;
... ...
class LoginForm extends Model
{
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
... ...
}
从代码可知,如果前面的验证规则都通过了才会进入密码正确性验证,系统会通过$this->getUser()
这个方法以username
为条件搜索用户表,并返回User
模型实例:
<?php
namespace common\models;
... ...
class LoginForm extends Model
{
protected function getUser()
{
if ($this->_user === null) {
$this->_user = User::findByUsername($this->username);
}
return $this->_user;
}
... ...
}
有了User
模型实例,就可以通过$user->validatePassword($this->password)
来对密码进行验证,验证通过后,会进入Yii::$app->user->login()
,将当前的认证信息切换到当前的用户,注意:
Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
此处$this->rememberMe
的作用就是维持用户的登录态:
- 0:浏览器关闭后或者手动清除session后,登录态失效;
- 1:登录态会保持到
3600 * 24 * 30
,即:30天后失效。
以后我们会有专门的章节对登录态的机制进行详细的讲解。
3.3 goBack()
登录成功后,会回到请求发起的页面重新请求一次,由于此时已经保存了用户认证信息,Yii::$app->user->isGuest
已经为非,因此会跳转到$this->goHome()
,进入系统Home
目录;
3.4 登录失败
如果登录失败,则会$model->password = '';
清除密码,返回到系统登录页面,提示如下信息,要求重新输入密码:
4 未完待续
关于系统是如何渲染出html元素,如何对用户输入的数据进行反应,报错信息如何动态展示,这些是都是小部件ActiveForm
的封装,它的行为取决于我们在Model类中对属性的约束,由于ActiveForm
比较复杂,我们会在小部件篇专门介绍,本文主要讲解登录机制,因此不作深入探索。
关注我的微信公众号,更多推送不遗漏!