zoukankan      html  css  js  c++  java
  • Yii2.0登录详解(下)

    在上一篇博文中,笔者讲述了yii2应用用户登陆的基本方法,但是这些方法到底是怎样实现登陆的呢?底层的原理到底是什么?在这篇博文笔者将从Yii的源码角度分析登陆的基本原理以及cookie自动登陆的原理,通过源码的分析,各位对Yii的理解也会更上一层楼。

    一、第一次正常登陆

         1、在LoginForm.PHP中,我们曾经调用了这个方法:

    [php] view plain copy
     
     print?
    1. Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);  

    由以上可知,调用了user组件的login()方法,把两个参数传递进入,分别是User类实例以及记住时间(Cookie验证会用到,如果传递0,则不启用Cookie验证)。

         2、进入yiiwebuser类中,找到login()方法如下所示:

    [php] view plain copy
     
     print?
    1. public function login(IdentityInterface $identity, $duration = 0)  
    2.     {  
    3.         if ($this->beforeLogin($identity, false, $duration)) {  
    4.             $this->switchIdentity($identity, $duration);     //①  
    5.             $id = $identity->getId();  
    6.             $ip = Yii::$app->getRequest()->getUserIP();  
    7.             if ($this->enableSession) {  
    8.                 $log = "User '$id' logged in from $ip with duration $duration.";  
    9.             } else {  
    10.                 $log = "User '$id' logged in from $ip. Session not enabled.";  
    11.             }  
    12.             Yii::info($log, __METHOD__);  
    13.             $this->afterLogin($identity, false, $duration);  
    14.         }  
    15.   
    16.         return !$this->getIsGuest();  
    17.     }  

     这里关注①号处代码:$this->switchIdentity($identity,$duration).这里调用了当前类的switchIdentity方法,把接受到的两个参数同时传递进去,我们往下看:

    3、switchIdentity($identity,$duration)方法如下:

    [php] view plain copy
     
     print?
    1. public function switchIdentity($identity, $duration = 0)  
    2.     {  
    3. <span style="white-space:pre">    </span>...  
    4. <span style="white-space:pre">    </span>...  
    5.         if ($identity) {  
    6.             ...  
    7.             if ($duration > 0 && $this->enableAutoLogin) {<span style="white-space:pre">    </span>//①  
    8.                 $this->sendIdentityCookie($identity, $duration); //②  
    9.             }  
    10.         } elseif ($this->enableAutoLogin) {  
    11.             Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie));  
    12.         }  
    13.     }  

    由于代码过长,笔者做了适当精简,只讨论与登陆和cookie联系最为密切的部分,由①处代码,首先会对duration进行判断,只有大于0的情况下才会进行cookie验证,然后再判断了enableAutoLogin的值,这个值也是cookie验证的关键所在,只有为true的时候才会储存cookie,该值在config/main.php中注册user组件的时候进行初始化,代码如下:

    [php] view plain copy
     
     print?
    1. 'components' => [  
    2.         'user' => [  
    3.             'identityClass' => 'appmodulesackendmodelsUser',  
    4.             'enableAutoLogin' => true,  
    5.         ],]  

    在判断都为真的时候,即进行cookie储存和登陆的时候,进入②号代码,可以看到,调用了sendIdentityCookie()方法。

    4、sendIdentityCookie($identity,$duration):

    [php] view plain copy
     
     print?
    1. protected function sendIdentityCookie($identity, $duration)  
    2.     {  
    3.         $cookie = new Cookie($this->identityCookie);  
    4.         //cookie的value值是json数据  
    5.         $cookie->value = json_encode([  
    6.             $identity->getId(),  
    7.             $identity->getAuthKey(),  
    8.             $duration,  
    9.         ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);  
    10.         //设置cookie的有效时间  
    11.         $cookie->expire = time() + $duration;  
    12.         Yii::$app->getResponse()->getCookies()->add($cookie);  //①  
    13.     }  

    在生成一个cookie的实例以及对cookie进行初始化后,接下来来到了重点部分,即①号代码处,首先getResponse()得到response组件,然后调用response的getCookies方法,返回一个CookieCollection实例,该实例保存了response组件所生成的所有cookie,显然,最后一个add()方法是把当前设置好的cookie保存进CookieCollection实例中。
    到目前为止,yii已经完成了储存cookie的操作,但是,有一点要注意的,利用yii框架的这种方式储存cookie,要记住一点,就是在Controller那里,通过验证后,应该使用redirect()来进行页面跳转,而不应该直接render()渲染布局,否则,当前登陆完成后,cookie将暂时得不到保存,(详细可通过查看浏览的cookie缓存来查看)如果cookie得不到立即的储存,有可能对后续用户的登陆造成未知困扰。

    为什么会出现这样的情况呢?

    我们来看看response组件的redirect()方法,(一般通过Yii::$app->response->redirect()来跳转url):

    [php] view plain copy
     
     print?
    1. public function redirect($url, $statusCode = 302, $checkAjax = true)  
    2.     {  
    3.         ...  
    4.         ...  
    5.   
    6.         return $this;  
    7.     }  

    关键在于最后的return $this;这句表示把当前类作为整个反应返回给客户端,换句话说:即当前保存的所有cookie,以及各种其他属性,在调用了redirect后全部返回。所以,我们在控制器需要使用redirect(),而不是直接使用render()方法。
    综上所述,在完成了user组件的login()方法后,用户的个人信息便保存于user组件中,直到用户关闭浏览器或者退出登录。


    二、利用Cookie登陆

    在用户关闭浏览器的时候(非退出),再次访问登陆页面,会发现页面已经自动跳转到主页,也即是说完成了自动登陆的功能(前提是点击了Remember Me),我们回顾一下logincontroller里面关于登陆的逻辑:

    [php] view plain copy
     
     print?
    1. if (!Yii::$app->user->isGuest) {  
    2.             return $this->goHome();  
    3.         }  

    可以看出,当访问登陆页面的时候,会先执行判断,判断当前用户是否是游客,若不是,则直接跳转到主页。所以关于自动登陆的逻辑便隐藏在:Yii::$app -> user ->isGuest 中,我们查看user组件相关代码:

    [php] view plain copy
     
     print?
    1. public function getIsGuest()  
    2.     {  
    3.         return $this->getIdentity() === null;  
    4.     }  

    在以上方法中,调用了getIdentity()方法:

    [php] view plain copy
     
     print?
    1. public function getIdentity($autoRenew = true)  
    2.     {  
    3.         if ($this->_identity === false) {  
    4.             if ($this->enableSession && $autoRenew) {  
    5.                 $this->_identity = null;  
    6.                 $this->renewAuthStatus();  
    7.             } else {  
    8.                 return null;  
    9.             }  
    10.         }  
    11.   
    12.         return $this->_identity;  
    13.     }  

    由于user组件默认是开始session的,所以enableSession应该为true,所以会执行renewAuthStatus()函数:

    [php] view plain copy
     
     print?
    1. protected function renewAuthStatus()  
    2.     {  
    3.         ...  
    4.         if ($this->enableAutoLogin) {  
    5.             if ($this->getIsGuest()) {  
    6.                 $this->loginByCookie();  
    7.             } elseif ($this->autoRenewCookie) {  
    8.                 $this->renewIdentityCookie();  
    9.             }  
    10.         }  
    11.     }  

    结合之前的enableAutoLogin为true以及当前处于未登录状态,所以getIsGuest()返回真,所以最后会执行loginByCookie()方法,这也是核心所在:

    [php] view plain copy
     
     print?
    1. protected function loginByCookie()  
    2.     {<span style="white-space:pre">   </span>//从客户端读取cookie  
    3.         $value = Yii::$app->getRequest()->getCookies()->getValue($this->identityCookie['name']);  
    4.         if ($value === null) {  
    5.             return;  
    6.         }  
    7. <span style="white-space:pre">    </span>//由于之前储存cookie是用json格式储存,所以现在需要先解析  
    8.         $data = json_decode($value, true);  
    9.         if (count($data) !== 3 || !isset($data[0], $data[1], $data[2])) {  
    10.             return;  
    11.         }  
    12.   
    13.         list ($id, $authKey, $duration) = $data;  
    14.         /* @var $class IdentityInterface */  
    15.         $class = $this->identityClass;<span style="white-space:pre">   </span>//读取当前的用户验证类类名,即实现了Identity接口的类  
    16.         $identity = $class::findIdentity($id);<span style="white-space:pre">  </span>//调用该类的方法,从数据库查找数据  
    17.         if ($identity === null) {  
    18.             return;  
    19.         } elseif (!$identity instanceof IdentityInterface) {  
    20.             throw new InvalidValueException("$class::findIdentity() must return an object implementing IdentityInterface.");  
    21.         }  
    22. <span style="white-space:pre">    </span>//如果数据库提供的auth_key与从客户取得的auth_key相同  
    23.         if ($identity->validateAuthKey($authKey)) {  
    24.             if ($this->beforeLogin($identity, true, $duration)) { //①  
    25.                 $this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0);  
    26.                 $ip = Yii::$app->getRequest()->getUserIP();  
    27.                 Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);  
    28.                 $this->afterLogin($identity, true, $duration);  
    29.             }  
    30.         } else {  
    31.             Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);  
    32.         }  
    33.     }  

    由①可知,在auth_key验证通过后,所执行的代码基本与首次执行的login()方法相同,即实现了重新登录的功能。

    关于Yii2用户登陆功能的实现以及cookie自动登陆的原理已经全部讲述完毕。

    ps:本文转自他人,不是原创,我还没那技术。

  • 相关阅读:
    List of the best open source software applications
    Owin对Asp.net Web的扩展
    NSwag给api加上说明
    'workspace' in VS Code
    unable to find valid certification path to requested target
    JMeter的下载以及安装使用
    exception disappear when forgot to await an async method
    Filter execute order in asp.net web api
    记录web api的request以及response(即写log)
    asp.net web api的源码
  • 原文地址:https://www.cnblogs.com/chenhaoyu/p/5949554.html
Copyright © 2011-2022 走看看