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

一、第一次正常登陆

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

  1. Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);

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

2、进入yii\web\user类中,找到login()方法如下所示:

  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. return !$this->getIsGuest();
  16. }

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

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

  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组件的时候进行初始化,代码如下:

  1. 'components' => [
  2. 'user' => [
  3. 'identityClass' => 'app\modules\backend\models\User',
  4. 'enableAutoLogin' => true,
  5. ],]

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

4、sendIdentityCookie($identity,$duration):

  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):

  1. public function redirect($url, $statusCode = 302, $checkAjax = true)
  2. {
  3. ...
  4. ...
  5. return $this;
  6. }

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

二、利用Cookie登陆

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

  1. if (!\Yii::$app->user->isGuest) {
  2. return $this->goHome();
  3. }

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

  1. public function getIsGuest()
  2. {
  3. return $this->getIdentity() === null;
  4. }

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

  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. return $this->_identity;
  12. }

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

  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()方法,这也是核心所在:

  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. list ($id, $authKey, $duration) = $data;
  13. /* @var $class IdentityInterface */
  14. $class = $this->identityClass;<span style="white-space:pre">   </span>//读取当前的用户验证类类名,即实现了Identity接口的类
  15. $identity = $class::findIdentity($id);<span style="white-space:pre">  </span>//调用该类的方法,从数据库查找数据
  16. if ($identity === null) {
  17. return;
  18. } elseif (!$identity instanceof IdentityInterface) {
  19. throw new InvalidValueException("$class::findIdentity() must return an object implementing IdentityInterface.");
  20. }
  21. <span style="white-space:pre">    </span>//如果数据库提供的auth_key与从客户取得的auth_key相同
  22. if ($identity->validateAuthKey($authKey)) {
  23. if ($this->beforeLogin($identity, true, $duration)) { //①
  24. $this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0);
  25. $ip = Yii::$app->getRequest()->getUserIP();
  26. Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);
  27. $this->afterLogin($identity, true, $duration);
  28. }
  29. } else {
  30. Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
  31. }
  32. }

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

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

Yii2.0登录详解(下)相关推荐

  1. linux 帐号 配置sftp_Linux 下sftp配置之密钥方式登录详解

    Linux下sftp配置之密钥方式登录 由于vsftp采用明文传输,用户名密码可通过抓包得到,为了安全性,需使用sftp,锁定目录且不允许sftp用户登到服务器.由于sftp使用的是ssh协议,需保证 ...

  2. Hadoop3.2.0使用详解

    Hadoop3.2.0使用详解 1.概述 Hadoop3已经发布很久了,迭代集成的一些新特性也是很有用的.截止本篇博客书写为止,Hadoop发布了3.2.0.接下来,笔者就为大家分享一下在使用Hado ...

  3. Android客户端实现注册/登录详解(一)

    前言 我们在开发安卓App时难免要与服务器打交道,尤其是对于用户账号信息的注册与登录更是每个android开发人员必须掌握的技能,本文将对客户端的注册/登录功能的实现进行分析,不到之处还请指出. 在这 ...

  4. [转载]AxureRP 7.0部件详解(一)

    转载]AxureRP 7.0部件详解(一) 本文为Axure RT7.0教程,本章主要介绍menu菜单.table表格.Tree Widget 树部件三个部件,后续将持续更新...... Menu 菜 ...

  5. Android消息传递之EventBus 3.0使用详解

    前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ...

  6. python获取登录后的cookie_python爬虫使用cookie登录详解

    前言: 什么是cookie? Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密). 比如说有些网站需要登录后才能访问某个页面,在登录之前,你想 ...

  7. android平台下OpenGL ES 3.0实例详解顶点属性、顶点数组

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  8. SeleniumLibrary4.5.0 关键字详解(五)

    SeleniumLibrary4.5.0 关键字详解(五) 库版本:4.5.0 库范围:全局 命名参数:受支持 简介 SeleniumLibrary是Robot Framework的Web测试库. 本 ...

  9. kubernetes,service详解下

    kubernetes,service详解下 HeadLiness类型的Service 在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,k ...

最新文章

  1. TOMACAT源码解析
  2. android实现华为手机拍照上传_继续引领手机拍照 华为将带来液态镜头
  3. 菜鸟学Java(十一)——GET与POST
  4. 获香港证监会颁发牌照的弘量研究,正用智能投顾帮助金融机构降低成本,提升资产管理能力 By 藤子2017年10月09日 17:16 撰文 | 藤子 2015 年,雷春然和黄耀东都是在香港科技大学的
  5. 《Unity 3.x游戏开发实例》——2.10节注意
  6. python 函数参数注解_python-如何使用函数注释来验证函数调用类...
  7. 实现单词大小写不敏感的正则表达式的匹配!
  8. Hello Quartz (第三部分)
  9. Linux下kvm:检测硬件是否支持虚拟化
  10. 三维家可以导入别人的方案吗_三维家精准赋能月兔家居打通前后端一体化,以时间匠空间!...
  11. linux命令4--rmrmdir
  12. Newtonsoft 六个超简单又实用的特性【上下篇】
  13. 036多级节点实现层叠展开与收缩的功能
  14. dimens文件生成器
  15. 开发者百宝箱——DevToys
  16. C语言中getchar
  17. 计算机毕业设计jsp宠物美容网站
  18. 戏子推荐伤感个性日志:其实我很脆弱
  19. 泰山OFFICE技术讲座:字体的kerning是什么意思
  20. Python下Spyder安装方法

热门文章

  1. 《出版专业基础》2015年版(初级)思考与练习 第八章
  2. 2009北京市小学生程序设计友谊赛详细答案
  3. 话机耳机模式进入保持状态下每隔1分钟会切换到免提模式后再切回耳机模式
  4. java 常用 函数式接口_「java8系列」神奇的函数式接口
  5. python基础数据类型练习2
  6. 机器学习-8(单调函数)
  7. 2018年终特辑:美通社TOP20最受媒体与公众关注新闻稿
  8. 2022-2028年中国城市轨道交通运维检修行业市场专项调研及竞争战略分析报告
  9. 金融工程对计算机的要求,金融工程专业对计算机的能力要求到底是什么?
  10. 大数据云图:如何在大数据时代寻找下一个大机遇 - 电子书下载(高清版PDF格式+EPUB格式)...