以YII2自带的登录代码为例
//这是控制器的登录动作
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
}
return $this->render('login', [
'model' => $model,
]);
以上依次调用了load和login方法,load方法为验证数据的合法性还有用户的密码:
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'], //这里调用了validatePassword方法去验证密码
];
}
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
第二个login登录方法如下:
//这里是LoginForm模型类
public function login()
{
if ($this->validate()) {
//这里对login传进了用户实例和rememberMe标识
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
}
return false;
}
其中传入getUser()和以rememberMe标识传入时间,getUser()方法则是返回用户表的实例:
public function getUser()
{
if ($this->_user === false) {
$this->_user = UserExt::findByUsername($this->username);
}
return $this->_user;
}
然后又继续调用YII的user组件的login方法进行登录:
public function login(IdentityInterface $identity, $duration = 0)
{
if ($this->beforeLogin($identity, false, $duration)) {
//这里进行主要的登录逻辑
$this->switchIdentity($identity, $duration);
$id = $identity->getId();
$ip = Yii::$app->getRequest()->getUserIP();
if ($this->enableSession) {
$log = "User '$id' logged in from $ip with duration $duration.";
} else {
$log = "User '$id' logged in from $ip. Session not enabled.";
}
Yii::info($log, __METHOD__);
$this->afterLogin($identity, false, $duration);
}
return !$this->getIsGuest();
}
其中switchIdentity方法就是处理登录以及登出的逻辑,相关的方法如下:
public function switchIdentity($identity, $duration = 0)
{
$this->setIdentity($identity);
if (!$this->enableSession) {
return;
}
/* Ensure any existing identity cookies are removed. */
if ($this->enableAutoLogin && ($this->autoRenewCookie || $identity === null)) {
$this->removeIdentityCookie();
}
$session = Yii::$app->getSession();
if (!YII_ENV_TEST) {
$session->regenerateID(true);
}
$session->remove($this->idParam);
$session->remove($this->authTimeoutParam);
if ($identity) {
$session->set($this->idParam, $identity->getId());
if ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
if ($this->absoluteAuthTimeout !== null) {
$session->set($this->absoluteAuthTimeoutParam, time() + $this->absoluteAuthTimeout);
}
//这里则根据是否开启了自动登录(在配置文件配置)以及上面传的rememberMe参数判断是否进行自动登录
if ($this->enableAutoLogin && $duration > 0) {
$this->sendIdentityCookie($identity, $duration);
}
}
}
主要是清除之前存在的cookie再把新的用户实例写进cookie,如果rememberMe为true的前提下还会调用sendIdentityCookie方法发送有失效期的cookie:
protected function sendIdentityCookie($identity, $duration)
{
//解析Cookie
$cookie = Yii::createObject(array_merge($this->identityCookie, [
'class' => 'yii\web\Cookie',
'value' => json_encode([
$identity->getId(),
$identity->getAuthKey(),
$duration,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
'expire' => time() + $duration,
]));
//发送Cookie
Yii::$app->getResponse()->getCookies()->add($cookie);
}
这里就要求需要自行实现用户模型的getId和getAuthKey方法,这就是主要的登录行为的逻辑。
主动登录
如果在登录的时候提交了rememberMe参数,就会触发自动登录的行为,实现这个行为的方法在Yii的User组件里:
protected function loginByCookie()
{
$data = $this->getIdentityAndDurationFromCookie();
if (isset($data['identity'], $data['duration'])) {
$identity = $data['identity'];
$duration = $data['duration'];
if ($this->beforeLogin($identity, true, $duration)) {
//根据用户实例进行登录操作
$this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0);
$id = $identity->getId();
$ip = Yii::$app->getRequest()->getUserIP();
Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);
$this->afterLogin($identity, true, $duration);
}
}
}
protected function getIdentityAndDurationFromCookie()
{
//获取cookie并进行解析
$value = Yii::$app->getRequest()->getCookies()->getValue($this->identityCookie['name']);
if ($value === null) {
return null;
}
$data = json_decode($value, true);
//对cookie进行验证,成功则返回用户实例,失败则清空cookie
if (is_array($data) && count($data) == 3) {
list($id, $authKey, $duration) = $data;
/* @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
if ($identity !== null) {
if (!$identity instanceof IdentityInterface) {
throw new InvalidValueException("$class::findIdentity() must return an object implementing IdentityInterface.");
} elseif (!$identity->validateAuthKey($authKey)) {
Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
} else {
return ['identity' => $identity, 'duration' => $duration];
}
}
}
$this->removeIdentityCookie();
return null;
}
在getIdentityAndDurationFromCookie方法中的$data则为存在客户端的cookie数据
array(3) { [0]=> int(1) [1]=> string(32) "i2bbB1bIga_PGehlATvu-p1UrDuW5Lw7" [2]=> int(2592000) }
然后便是对cookie数据进行了验证并完成登录,验证的需要实现用户实例的findIdentity和validateAuthKey方法。
这里说明一下,用户实例是根据用户表模型自行创建的模型类,而User组件则是yii\web\user。对应的指南在这。