源码解析 - Yii2 User cookie 登录原理 [ 2.0 版本 ]
本文是在session登录畅通无阻的前提下,首先要先搞好session登录,这个在这里不做叙述,好多session登录的文章。本文叙述cookie登录的原理和使用
FancyEcommerce原文链接:Yii2 User cookie 登录原理
1.具体实现:
1.1 cookie登录配置config方面,配置好的代码如下:
'components' => [
'user' => [
'identityClass' => 'fecshop\models\mysqldb\Customer',
'enableAutoLogin' => true,
'authTimeout' => 3600,
],
],
enableAutoLogin设置为true,就会使用cookie登录,如果设置了enableAutoLogin,那么下面的超时时间authTimeout就会无效,因为这个参数是session的超时时间,不过,我们可以在登录的时候,超时时间也从这个配置参数读取,譬如下面的函数参数$duration,可以从这里读取,如果这样,就会有效。
$duration = \Yii::$app->user->authtimeout;
\Yii::$app->user->login($this->getCustomer(), $duration);
上面的结论通过代码解释,如下:
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();
}
查看 $this->switchIdentity($identity, $duration);
public function switchIdentity($identity, $duration = 0)
{
$this->setIdentity($identity);
if (!$this->enableSession) {
return;
}
/* Ensure any existing identity cookies are removed. */
if ($this->enableAutoLogin) {
$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);
}
if ($duration > 0 && $this->enableAutoLogin) {
$this->sendIdentityCookie($identity, $duration);
}
}
}
查看:$this->sendIdentityCookie($identity, $duration);
protected function sendIdentityCookie($identity, $duration)
{
$cookie = new Cookie($this->identityCookie);
$cookie->value = json_encode([
$identity->getId(),
$identity->getAuthKey(),
$duration,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$cookie->expire = time() + $duration;
Yii::$app->getResponse()->getCookies()->add($cookie);
}
通过一层层的函数可以看到,cookie的超时时间是从login()函数中读取出来的,
而不是从\yii\web\User的authTimeout
变量读取。最后传递给sendIdentityCookie($identity, $duration)
方法,进而设置cookie的超时时间。
另外配置中开启了enableAutoLogin,但是在调用login方法的时候没有设置超时时间变量$duration,同样不会设置cookie,进而配置 enableAutoLogin 无效,下面是代码解释:
public function switchIdentity($identity, $duration = 0){
#函数中的代码
if ($duration > 0 && $this->enableAutoLogin) {
$this->sendIdentityCookie($identity, $duration);
}
}
可以看到,如果超时时间为0,那么不会执行设置cookie的方法
public function sendIdentityCookie($identity, $duration);
进而不会设置cookie
1.2 代码改进:
参考代码:
public function getCustomer(){
if($this->_customer === null){
$this->_customer = Customer::findByEmail($this->email);
}
return $this->_customer;
}
public function login($duration = 86400)
{
if ($this->validate()) {
//return \Yii::$app->user->login($this->getAdminUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
return \Yii::$app->user->login($this->getCustomer(), $duration);
} else {
return false;
}
}
在上面的代码,默认cookie的过期时间为86400秒,这样默认就不会cookie超时
如果我我调用:
$model = new CustomerLogin;
$model->email = $data['email'];
$model->password = $data['password'];
$loginStatus = $model->login(0);
由于过期时间填写为0,因此,即使在user组件中开启配置enableAutoLogin=true,
cookie也不会生效。
另外,我如果从\yii\web\User的authTimeout变量读取。来设置cookie的超时时间,也是一个不错的选择,代码如下:
public function login($duration = 0)
{
if(!$duration){
if(Yii::$app->user->authTimeout){
$duration = Yii::$app->user->authTimeout;
}
}
if ($this->validate()) {
//return \Yii::$app->user->login($this->getAdminUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
return \Yii::$app->user->login($this->getCustomer(), $duration);
} else {
return false;
}
}
- cookie超时时间刷新。
在默认的情况下,如果登录成功账户,每次请求访问Yii::$app->user->identity,都会刷新cookie的超时时间,设置自动更新的变量为:
public $autoRenewCookie = true;
该变量默认为true,所以不需要在配置中设置这个变量,使用默认就好。
cookie超时时间刷新的原理解释,下面是详细代码:
执行Yii::$app->user->identity,对应的是下面的函数
public function getIdentity($autoRenew = true)
{
if ($this->_identity === false) {
if ($this->enableSession && $autoRenew) {
$this->_identity = null;
$this->renewAuthStatus();
} else {
return null;
}
}
return $this->_identity;
}
会执行renewAuthStatus()方法
protected function renewAuthStatus()
{
$session = Yii::$app->getSession();
$id = $session->getHasSessionId() || $session->getIsActive() ? $session->get($this->idParam) : null;
if ($id === null) {
$identity = null;
} else {
/* @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
}
$this->setIdentity($identity);
if ($identity !== null && ($this->authTimeout !== null || $this->absoluteAuthTimeout !== null)) {
$expire = $this->authTimeout !== null ? $session->get($this->authTimeoutParam) : null;
$expireAbsolute = $this->absoluteAuthTimeout !== null ? $session->get($this->absoluteAuthTimeoutParam) : null;
if ($expire !== null && $expire < time() || $expireAbsolute !== null && $expireAbsolute < time()) {
$this->logout(false);
} elseif ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
}
if ($this->enableAutoLogin) {
if ($this->getIsGuest()) {
$this->loginByCookie();
} elseif ($this->autoRenewCookie) {
$this->renewIdentityCookie();
}
}
}
如果enableAutoLogin开启,如果登录就会执行renewIdentityCookie方法。
protected function renewIdentityCookie()
{
$name = $this->identityCookie['name'];
$value = Yii::$app->getRequest()->getCookies()->getValue($name);
if ($value !== null) {
$data = json_decode($value, true);
if (is_array($data) && isset($data[2])) {
$cookie = new Cookie($this->identityCookie);
$cookie->value = $value;
$cookie->expire = time() + (int) $data[2];
Yii::$app->getResponse()->getCookies()->add($cookie);
}
}
}
该方法会重新设置超时时间,通过上面的几个函数知道了原理,那么问题来了,如果我登录了用户,超时时间设置的为10秒,我开始浏览器文章或者干其他的,每次访问间隔3秒,如果这些页面都没有执下面的行代码:Yii::$app->user->identity
那么,10秒后,用户登录状态就会被超时,需要重新登录,(有一些间接的方法也会执行上面的代码,譬如Yii::$app->user->isGuest ,就会间接调用执行Yii::$app->user->identity,这种情况下不会超时)。
public function getIsGuest()
{
return $this->getIdentity() === null;
}
因此,如果登录状态要持久下去,那么为了保持登录状态,每个页面请求后,都需要执行Yii::$app->user->identity,然后cookie的超时时间就会更新,这样3秒请求一次,登录状态会一直保持下去。
上面是yii2 cookie使用过程中要注意的一些问题。总体来说还是不错,但在实际过程中,还需要结合一下其他,譬如cart 和customer login都使用cookie,超时时间要一致,那么,cart和customer的cookie超时时间要一致,并且,在每次调用Yii::$app->user->identity,都必须执行更新cart的cookie超时时间,因此,需要重写Yii\web\User。
下一个章节介绍实现的具体步骤。
Fecshop 深圳
最后登录:2024-08-13
在线时长:73小时36分
- 粉丝157
- 金钱2381
- 威望490
- 积分8011
共 2 条评论
最后,推荐一下我的Fecshop ,开源商城,github地址:https://github.com/fancyecommerce/yii2_fecshop
演示地址:http://fecshop.appfront.fancyecommerce.com/
截止到2016-11-12号,产品,分类,首页,评论,用户中心,搜索,多语言,多货币 等功能已经做完,除了购物车和支付部分,其他的基本都已经完成,关注fecshop的 在等2-3个月,也就是明年2,3月份,版本已经就可以出来,2017年4,5月份在把手机web 做一下,预计到明年5月份,后台,pc前台,手机web前台 ,命令控制台 这几个入口 基本可以完善,多谢大家关注和你们的Star,谢谢,我会坚持把他写好。
作者QQ:2358269014
一直在关注,辛苦~
前后台用不同模块的话,分别实现登录,应该如何配置user组件呢,我在应用配置中配置了user组件指向后台admin管理员表,前台user用户表要登录,我在前台模块中配置components user时是照搬的应用下的user配置,但是会报错The configuration for the "user" component must contain a "class" element.
分别独立配置,譬如:https://github.com/fecshop/yii2_fecshop/blob/master/app/appfront/config/appfront.php
你这个报错是user 组价配置没有配置
class
你的前后台的class,可以一样,但是 identityClass 用不同的model
'user' => [ 'class' => 'fecshop\yii\web\User', 'identityClass' => 'fecshop\models\mysqldb\Customer', // 是否cookie 登录。 /* * @var boolean whether to enable cookie-based login. Defaults to false. * Note that this property will be ignored if [[enableSession]] is false. * 设置为true的好处为,当浏览器关掉在打开,可以自动登录。 */ 'enableAutoLogin' => true, /* * authTimeout => 56666, * 这里请不要设置authTimeout,为了让customer账户session * 和cart的session保持一致,设置超时时间请统一在session组件 * 中设置超时时间。 */ //'authTimeout' => 56666, ],