Cookie和Session、SessionID的那些事儿
事情的起因是这样的,前段时间一直忙着做业务需求,也没时间好好了解一下公司项目的架构设计和一些具体的设计是怎么实现的,正巧这两天碰上中秋国庆小长假,手头上的需求不是很紧张,闲来无事就想看看鉴权这块的逻辑是怎么做的,顺便好好了解和巩固一下session和cookie的工作流程,由于期间碰到了一些事情还挺有意思的,那就顺便记录下来,避免以后再忘记啦!
1.单一入口文件
首先啊,项目从public/index.php进来.
①先是定义了一些目录常量,例如:项目路径,临时文件路径,配置项路径,模块路径等等.
②其次引入两个文件:一个自动加载文件,一个全局函数定义文件(非必要的).
③接着就是新建一个DI,注入依赖了,比较合理的顺序是先引入config依赖,因为后面注入其他依赖的时候,可能会在闭包中使用到config依赖.具体的流程可以参考
④新建应用,注册多模块功能(非必要的)等等
2.dispatcher调度控制器
现在, 让我们看看应用程序是如何实现的. 首先我们知道有个组件叫做 Dispatcher. 通过 Routing 组件来找到路由. 然后, 它负责加载合适的控制器和执行相应的动作方法.
正常情况下, 框架会自动创建分发器. 对我们而言, 我们想在执行请求的方法之前执行一个验证, 校验用户是否可以访问它. 要做到这一点, 我们需要在启动文件中创建一个方法来替换组件:
<?php
use Phalcon\Mvc\Dispatcher;
// ...
/**
* MVC 分发器
*/
$di->set(
"dispatcher",
function () {
// ...
$dispatcher = new Dispatcher();
return $dispatcher;
}
);
我们现在使用完全控制的分发器用于应用程序. 在框架中需要多组件的触发事件, 允许我们能够修改内部的操作流. 依赖注入组件作为胶水的一部分, 一个新的叫做 EventsManager 的组件允许我们拦截由组件产生的事件, 路由事件到监听.
一个 EventsManager 允许我们为一个特定类型的事件添加监听. 现在我们感兴趣的类型是 “dispatch”.
现在我们就通过这个时间管理来控制我们的路由权限.
3.session和cookie
到这里我以为事情就快要结束了,但是事情远远没有我想像的那么简单
首先是获取当前路由的模块,控制器和方法
class Security extends Plugin
{
public $module;
public $controller;
public $action;
public function beforeDispatch(Event $event, Dispatcher $dispatcher)
{
$this->module = $dispatcher->getModuleName();
$this->controller = $dispatcher->getControllerName();
$this->action = $dispatcher->getActionName();
}
接着我们要获取当前用户的登陆信息了,实际上获取登陆信息很简单,一行代码就行了($key是登陆时,你自己设置的key)
$this->session->get($key);
但是问题在这里就展开了遐想,为什么通过这个唯一的key能让每个用户获取到不同的登陆信息?
了解过session的可能会知道,每个session都对应一个sessionID,服务端通过这个sessionID获取session,然后这个sessionID默认是存储在客户端的Cookie中.
好的,我们先来了解一下session和cookie
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。
而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。不可能每发送一次请求就要让用户输入一次账号密码吧?这不合理!要跟踪该会话,必须引入一种机制.
1.Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
2.除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。
在服务端保存Session的方法很多,内存、数据库、文件都有。当我们只有一台服务器的时候,使用默认的配置,放文件里就好了,但是,实际场景是为了解决高并发请求,我们经常会使用多台服务器,配置负载均衡,这样的话将session放在服务器的文件中就不合理了.因为客户的每次请求都可能会被负载均衡机制分发到不同的服务器,这样就会导致下次请求找不到用户对应的session,因此解决办法是把session放在redis中.
当客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法算出一个session的ID,用来标识该session对象,这个session会放在服务端,例如session_id作为key,session序列化后作为value,生成键值对放在redis中,或者session_id拼接上一个字符串作为文件名,session序列化后作为文件内容,生成一个文件放在目录下.然后将这个sessionID返回给客户端,客户端将他放在cookie中保存在浏览器.当浏览器禁用cookie时,session是不是就不能用了?
不是的.sessionid除了可以放在cookie中,还能放在URL中(作为URL附加信息或者URL参数字段),还能放在表单隐藏域中.
就说这么多了,不想抄了
4.session配置
另外说说今天踩得一个坑:
在本地环境中,运行phpinfo(),在输出信息中可以看到session是保存在redis中的,但是在本地的docker容器中,php.ini文件中记录的session是保存在file中的,很奇怪.
最后找到一篇博客,说是php-fpm.conf中的配置会覆盖php.ini中的相同配置,所以要注意配置文件不生效时,应该注意php-fpm文件是不是也有这样的配置!!!
配置php.ini把session存储到redis
①:在不修改任何配置的情况下。如果开启了session,可以通过 session_id() 函数来获取/设置当前会话 ID,于是我们利用这个ID来将session和会话信息存储到redis,这是基于用户态的方式,其实这种方式并没有改变PHP去创建文件,查找并读取文件的事实,因为这个只是在原来基础上再建立一层管理机制而已,不建议这种方式。
②:修改php.ini,注意修改 php-fpm.conf 文件
③:ini_set
④:session_set_save_handler()