Yii用户自动登陆流程

作者 | 2014年10月22日

前一段时间更改了站点的授权架构,今天被通知自动登陆功能不好使了。用的Yii框架,为了找出原因,借着机会把Yii的自动登陆流程理一遍。花了一个上午弄明白了流程,了解了原理之后简单几行代码就把问题解决了。

Yii的自动登陆基于cookie,从cookie中获取用户凭据,验证成功后授权并登陆用户。这篇文章 描述的是Yii的登录流程而非自动登陆流程。在解决过程中参考了这篇文章,以至于被误导了。

鉴于在网上没有找到非常好的Yii自动登陆流程分析资料,本文整理了Yii中用户的登陆流程,希望能对需要的人有所帮助。

流程

  1. 入口文件index.php创建了CWebApplication应用。当用到user组件时会通过Yii::app()->user方式获取。
  2. user不是CWebApplication的成员,由于在其父级(framework/base/CModule.php)中实现了 __get 方法,实际上访问的是getUser()方法;
  3. 打开 framework/web/CWebApplication,找到 getUser 方法,其原型为:

    public function getUser()
    {
    return $this->getComponent('user');
    }
    

该方法将请求委托给getComponent函数。

  1. 接着看 getComponent 方法的实现。方法位于 framework/base/CModule.php 文件中,其原型为:

    public function getComponent($id, $createIfNull=true)
    {
    if(isset($this->_components[$id]))
    return $this->_components[$id];
    else if(isset($this->_componentConfig[$id]) && $createIfNull)
    {
    $config=$this->_componentConfig[$id];
    if(!isset($config['enabled']) || $config['enabled'])
    {
    Yii::trace("Loading \"$id\" application component",'system.CModule');
    unset($config['enabled']);
    $component=Yii::createComponent($config);
    $component->init();
    return $this->_components[$id]=$component;
    }
    }
    }
    

    第二个参数默认为true,指明如果其不存在则自动创建组件。当用户访问站点,后台获取用户信息访问 user 组件就会触发该段代码。如果user对象存在,直接返回,如果不存在,根据配置创建组件: $component=Yii::createComponent($config);.

  2. user的配置在文件(protected/config/main)中,默认的实例类型为系统实现的CWebUser。另外可配置是否开启自动登录,登录网址等;

  3. 组件创建完毕后,开始执行init方法。在 framework/web/auth/CWebUser.php下找到 init方法,其原型为:

    <

    pre class=”lang:php”>public function init() { parent::init();

    Yii::app()->getSession()->open(); if($this->getIsGuest() && $this->allowAutoLogin) $this->restoreFromCookie(); else if($this->autoRenewCookie && $this->allowAutoLogin) $this->renewCookie(); if($this->autoUpdateFlash) $this->updateFlash();

    $this->updateAuthStatus();

}

如果配置开启了 `allowAutoLogin`属性,就进入 `restoreFromCookie`。
  1. 同样在 framework/web/auth/CWebUser.php 文件里, restoreFromCookie 的实现如下:

    protected function restoreFromCookie()
    {
    $app=Yii::app();
    $request=$app->getRequest();
    $cookie=$request->getCookies()->itemAt($this->getStateKeyPrefix());
    if($cookie && !empty($cookie->value) && is_string($cookie->value) && ($data=$app->getSecurityManager()->validateData($cookie->value))!==false)
    {
    [email protected]($data);
    if(is_array($data) && isset($data[0],$data[1],$data[2],$data[3]))
    {
    list($id,$name,$duration,$states)=$data;
    if($this->beforeLogin($id,$states,true))
    {
        $this->changeIdentity($id,$name,$states);
        if($this->autoRenewCookie)
        {
            $cookie->expire=time()+$duration;
            $request->getCookies()->add($cookie->name,$cookie);
        }
        $this->afterLogin(true);
    }
    }
    }
    }
    

    该方法的逻辑为: 取出用户的cookie信息-》进行合法性校验-》解压数据-》准备登录 beforeLogin

  2. 默认 beforeLogin 直接返回true, 接着进入了 changeIdentity 的阶段。下面是该函数的实现:

    protected function changeIdentity($id,$name,$states)
    {
    Yii::app()->getSession()->regenerateID(true);
    $this->setId($id);
    $this->setName($name);
    $this->loadIdentityStates($states);
    }
    

    主要是加载用户的id和username。此时,系统已经可以通过 Yii::app()->user 获取到用户的信息,isGuest函数返回false。

  3. 用户授权完成之后,就是 afterLogin 后处理方法。方法可以用来做一些后续工作,比如记录用户的登录时间,IP。至此,用户自动登陆工作全部完成。

对比用户名密码登陆,自动登录是基于cookie的。校验cookie通过后,生成授权实例,再登录系统。

一些要点

  • 根据源码跟踪,在 UserIdentity 里的属性都会被序列化之后存储到cookie里面,同时这些属性也会放到用户的session中。默认的属性是id和name,可以根据需要将其他属性放入,例如用户角色。

  • 重写 afterLogin 方法是必要的,可以在自动登陆之后做许多的事情,例如根据用户角色实例化用户对象等。

以上就是个人捕捉到的一些细节,如果有误欢迎指正。

发表评论

电子邮件地址不会被公开。 必填项已用*标注