COOKIE、SESSION 之彻底搞定
前言:
其中 web 服务器中有好多脚本页面,即一个网站有好多页面,我们客户端的一个用户比如“张三”的浏览器去
访问“脚本 1”完以后,继续去访问“脚本 2”的时候,这两次访问之间是没有任何联系的,是独立的请求,也就是说
当我们访问一个网站的第一个页面和第二个页面的时候,HTTP 协议的内部机制是无法检测或者告诉我们这两次访
问是否是同一个用户进行的,也就是说在第一个页面中访问的信息(比如:登录信息)是无法在访问第二个页面
的时候带过去的,这就是所谓的“HTTP 协议的无状态”。
因为变量只能在一个页面中使用,如下方法可以做到信息在页面之间的传递和共享:
1. get 和 post 方法传递, 两个页面之前传递(不是跟踪用户的首选)
2. 将变量存在文件或是数据库中 (所有人共用变量, 也不跟踪用户的首选)
既然这样也就无法做到用户信息的跟踪,而解决这个问题就必须要引出会话控制,而会话控制就可以解决多
页面用户信息的跟踪。让一个用户访问每个页面,服务器都知道是那个用户在访问。
一、COOKIE:
1. 深入理解 cookie 概念:
1. 理念引入:
1. 服务器给客户端的礼物
2. 比如说:我们需要看自己的注册资料,即用户看自己的信息。
连上 mysql 数据库查询,然后 get 传参 user_id,根据 user_id 查询用户信息。
思考:如果我的 user_id 是 5,我在地址栏输入 5,看到的是自己的信息,那输入 6 不就看
到比人的信息了么。
2. 比如说:一群人买豆浆,也不排队乱哄哄的,喝完了豆浆才交钱,老板怎么区分哪个人喝的什么豆呢?
思考:用什么方法来解决这个问题。
方案:每当有人过来拿豆浆时,就给他一个小纸条,上面写着那个人喝什么豆浆。
2. 必须性导入:
问题 1:如何才能控制只安全方便的看到自己的信息呢?
问题 2:有个页面很重要,不是网站的用户,不能看。
思路 1:当我们登录成功的时候就定义个常量,然后在这个页面判断这个常量是否为 true。
如果没有登录而直接跳到这个页面的话,自然没有这个常量,这个时候让它直接跳出。
但是这个思路又出现以下问题:
1. 无法跟踪到底是哪个用户进行了登录,因为谁登录成功都会定义这个常量
2. 无法跨页面,因为一个页面的脚本执行完毕以后所有的常量变量全部被销毁来释放空间。
:
思路 2:我们可以用 GET 传参来跟踪,但是这个页面多的话,是不是太烦了,而且地址栏传参极度不安全。
经过一番折腾,我们需要一个即可以跨页面,也可以随时都记住用户,又安全的一个全局变量,那么我
们伟大 cookie 便产生了。
当你请求 a.php,给 apache 服务器发送 a.php 的访问请求以后,服务器响应的内容中发现,服务器在客户端
设置了一个 cookie,名字为 username,值为 zhangsan:
当再次请求服务器的时候,客户端中的 cookie 信息通过 http 协议传给服务器,然后这些值存放在
超全局数组$_COOKIE 中。如果用的话,随时可以取出来使用:
总结 cookie 运行流程如下图:
服务器给浏览器:发(服务器响应 http 的时候)
浏览器给服务器:发(浏览器向服务器发 http 的时候) -------> 收(服务器接受到浏览器 http 请求的时候)
-------> 存(客户端接受 http 响应的时候) ------->
2. cookie 设置读取与销毁:
1. 服务器如何给浏览器设置 cookie?客户端发给服务器 cookie 后,服务器如何读?
答案:在 PHP 中,服务器设置 cookie 用 setcookie()函数,在 PHP 中,读取 cookie 不用特殊方法,因为 cooki
的信息已经被 PHP 处理到了$_COOKIE 这个超级全局数组里,直接读取$_COOKIE 就行了。
2. 思考:
1. cookie 里设置的变量名是什么?
2. cookie 里设置的变量值是什么?
3. cookie 里的变量有效期到什么时间?
4. 在 sohu.com 下设置的 cookie 在哪儿有效呀?
1、2、3 的答案:
4 的答案:
1. 这个问题涉及到 cookie 中的第 4 个参数,也就是 cookie 的作用域。一个页面设置的 cookie,默认
在其同级目录下以及子目录下起作用(可以读取)。如果想让 cookie 整站有效,可以在根目录下
setcookie,也可以用第 4 个参数来指定 cookie 生效路径,比如“/” 。
2.
cookie 是不能跨域名的(否则安全问题就太大了),比如:sohu.com 的 cookie 是不能被发到 sina.com
用的!但是可以在一个域名的子域名下使用,需要用第 5 个参数来指定。
setcookie('key' , 'value' , time()+200 , '/' , '.sina.com.cn');
这个 cookie 在 book.sina.com.cn,mili.sina.com.cn 以及 xxx.sina.com.cn 都可以用
3. 销毁或者让 cookie 失效:
方法一: setcookie('key' , '' , 0);
方法二: setcookie('key' , '' , time()+一个负数);
3. cookie 案例之计数器与浏览历史:
见 php 文档。
二、SESSION:
1、由 cookie 引入 session:
上面我们学习了 cookie,那么简单回顾一下 cookie 的用法:
a.php 页面中我们事先保存一个 cookie 值:
setcookie('user','list');
b.php 页面中我们做一个实验,根据 cookie 的 user 的值来确定某网站用户的级别:
if ($_COOKIE['user'] == 'vip') {
echo '尊敬的大客户';
}else{
echo '普通的小客户';
}
当实验进行到这儿的时候,浏览器中顺利输出了‘普通的小客户’,但是我们接着做如下操作:
打开 firefox 浏览器,F12 打开 firebug,重新用该浏览器运行 a.php 页面,做如下操作:
点下面的确定以后,再次运行 b.php,这个时候,原来 a.php 在 cookie 中保存的 user 的值在 http 发送的时候就变成
了我们手动修改为的 vip,而浏览器也成功的输出了‘尊敬的大客户’,很显然我们成功的修改了用户的级别,也就
是说 cookie 是不安全的。
实例引入:(客户就相当于客户端,老板就相当于服务器)
旧思路:对于 cookie,相当于蛋糕店的老板给你一张纸,纸上写着你领取的物品:蛋糕,奶酪等等。这个纸片在
你手里,所以容易篡改,刚才大家已经看到了上面的篡改操作。
新思路:你买了蛋糕后,老板给你一张收据,收据上写着:‘编号 89757’,你取物品时,老板打开账本核对‘编号
89757’以后发现对应物品是:蛋糕一份,取出蛋糕给你。这一次就不好伪造了,因为真正的收据在老板
手里。
简而言之:
cookie 很容易被用户篡改或者假冒发送,所以它一般用在安全系数不是很高的地方,比如用户名,用户浏览
记录等等。既然这样那么我们应该怎么防范或者解决这个问题呢?
我们的 session 技术闪亮登场!
2. session 运行流程:
1. session 开启需要以下步骤:
1. 开启 session 功能 (我需要一个柜子)
2. 给 session 赋值 (往柜子里装东西)
3. 把 session_id 传给浏览器 (给顾客该柜子的编号)此步骤由服务器和浏览器自动完成不用我们操心。
2. session 读取需要以下步骤:
1. 浏览器把 session_id 传给服务器 (柜子的编号拿出来)
2. 服务器把和 session_id 匹配的 session 信息返回给浏览器(用编号打开对应的柜子拿东西)
3. 修改 session 的信息在服务器端保存的位置:
打开 php.ini :
session.save.path = “D:/tmp” 这里的 tmp 文件件必须事先创建好。
4. 当我们运行 a.php 的时候,服务器响应浏览器的时候在客户端自动设定了与 session 值对应的 PHPSESSID.
当浏览器没有关闭的情况下,再次请求服务器的时候,这个时候客户端的浏览器会带着存储在客户端 cookie
中的 PHPSESSID 去服务器中存放 SESSION 值的文件中找和它对应的值,如果有的话直接用$_SESSION[‘键’] 可
以取出。
这就是 session 的整个工作流程。
3. session 详细语法学习:
1. 无论是创建,修改还是销毁 session 都需要先 session_start(),以便让 PHP 的核心程序将和 session 相关的内建环
境变量预先载入到内存中,所以当第一次 session_start()那一刹那 session_id()跟着创建,这个时候这个 session_id()
就会保存在 cookie 中,就相当于你去存包的时候,只拿一个带着编号二维码的纸条自己装着就行,取包的时候
出示对比就行。如果在其他页面需要开启 session 的时候,第二次以上 session_start()的时候,这个时候它会把
第一次 session_start()的时候创建的时候 session_id()返回。如果是基于 cookie 的 session 在开启会话之前不能有
任何输出。(session 有两种:基于 cookie 和基于 url)。
它的作用有两个:第一,开启一个会话,第二,返回一个存在的会话。
保存在客户端的 PHPSESSID 值如下:
session_id()
== $_COOKIE[session_name()]
== $_COOKIE[‘PHPSESSID’]
2. 一旦 session_start 之后,$_SESSION 就可以自由的添加,删除,修改,当成普通的数组一样操作(这一点和 cookie
的操作不一样,只能通过 setcookie 函数来进行)
3. 销毁 session:
第一、2 个思路:
客户端 1 个方法:销毁 PHPSESSID。
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(),'',time()-3600 , '/');
}
服务器端 4 个方法:第一、把箱子清空。 第二、把箱子烧毁 .
1:可以单独销毁某一个单元,即把$_SESSION 数组某一个单元 unset 掉:
$_SESSION[‘key’] = ‘’ ;
unset($_SESSION[‘school’]);
2:可以整体把箱子清空,即$_SESSION 数组给清空,即存放 session 文件变为空白了,但是这个
文件还在。
$_SESSION = array();
3:利用函数把箱子整体清空,效果和 2 相同,文件为空白,但是文件还在。
session_unset();
4:彻底把箱子给毁掉。
session_destory();
//彻底销毁 session 文件。
第二、彻底销毁 session 的经典用法四部曲:
session_start();
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(),'',time()-3600 , '/');
}
$_SESSION = array();
session_destory();
为什么我们不直接销毁 session_id()呢?因为我们的目的要把客户端的存在 cookie 中的 session_id 销
毁,再把服务器中的 session 数组也清空,而不留下冗余数据。
4. session 关于 php.ini 配置项:
我们知道一个 session 有 2 方面的数据共同发挥作用:
1:客户端的 cookie
2:服务器端的 session 文件
要想让 session 失效也是要从这 2 个角度来考虑的。
当 session.auto_start = 1 的时候,每个页面不再用 session_start()就可以自动开启,但我们一般不建议利用配置文件
开启 session,因为这样的话 PHP 的一些功能也就受到限制执行不了了,这个时候便不能把对象放入 session 当中
了,因为类的定义必须在启动 session 之前加载,而自动设为 1 的时候,某页面的类还未加载 session 就已经是开
启 状态了。
在 php.ini 里,下面的选项控制 PHPSESSID 的 cookie 的生命周期,以秒为单位。
注意:如果用户篡改了 cookie,让 cookie 的生命周期为 1 年,那你也判断不出来。
如果想严格的让 sesion 就半个小时有效,可以这样:
$_SESSION[‘time’] = 登录时的时间戳
检验 session 的开启时间。
为什么 PHPSESSID 对应的 cookie 失效以后,对应的 SESSION 也会跟着失效呢?因为虽然服务器中保存着 session
信息,但是客户端浏览器中的 PHPSESSID 已经没有了,无法返回给服务器了,也就找不到对应的 SESSION 值了,就
相当于银行里有钱,但是银行卡丢了,钱还是无法取出来。
思考:
如果浏览器中的 PHPSESSID(cookie)失效了,难道服务器中的 SESSION 值还保存着一直占用着空间吗?
答案:
当然不是啦,因为 session 会定期的清理内存中的垃圾文件。