日期:2014-05-17  浏览次数:20591 次

PHP Session原理分析及使用

???? 之前在一个叫魔法实验室的博客中看过一篇《php session原理彻底分析》的文章,作者从session的使用角度很好阐述了在代码运行过程中,每个环节的变化以及相关参数的设置及作用。本来想把原文转帖过来,但是原博客被关闭了。不知是这次大范围的重新备案,还是其他什么原因所致。通过百度快照找到一些原文资料,没找到的将按之前的理解重新整理,以使大家对session能有更多了解。

楔子:Session大白话

Session,英文翻译为“会话”,两个人聊天,从第一句问好,到最后一句再见,这就构成了一个会话。PHP里的session主要是指客户端浏览器与服务端数据交换的对话,从浏览器打开到关闭,一个最简单的会话周期。计算机语言一般怎么实现会话呢?举个通俗的例子:
服务端好比一个理发店,客户端好比每一个去理发的客人,很多理发店都有这种促销手段,连续消费10次的客人,可以免费一次,大概有三种方式来实现:
1、理发师傅记性太好,你来过几次,他看一眼就知道——这叫协议本身支持会话;
2、每个客人发一个会员卡,你每次消费,都要带着这个卡片,消费一次记录一笔,当然还要加盖印章——这叫通过cookie实现会话,缺点是安全性不高,我完全可以伪造会员卡或者公章;
3、理发店准备一个大帐本,客人每人对应一个会员号或者自己的个人资料,甚至密码,每个客人来消费,报一下自己的会员号,再把消费次数记录到大帐本里——这就是session实现会话,客人脑子里的会员号就是保存在客户端的SESSIONID,大帐本就是保存在服务端的session数据,这样相比第二种方法,安全性要高很多,除非你说你把自己的会员号和密码都搞丢了,这叫做伪造客户端的SESSIONID。
?
因为http协议是无状态的,所以php要实现会话只能通过后面两种方式,前一种cookie,缺点已经说了,安全性不高,所以重要的会话会选择使用session。session会话必须依靠一个标识,也可以理解成一个暗号,就是SESSIONID。这是个经过加密的串,保存在客户端,通常在cookie里,客户端与服务端的每次交流都是通过这个SESSIONID,客户端先自报家门,服务器才能找到你在服务端保存的会话数据,继续通话。

php.ini常用session设置

[服务端]
session.save_handler = files
默认为file,定义session在服务端的保存方式,file意为把sesion保存到一个临时文件里,如果我们想自定义别的方式保存(比如用数据库),则需要把该项设置为user;
?
session.save_path = "/tmp/"
定义服务端存储session的临时文件的位置。
?
session.auto_start = 0
如置1,则不用在每个文件里写session_start(); session自动start。
?
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440
这三个配置组合构建服务端session的垃圾回收机制session.gc_probability与session.gc_divisor构成执行session清理的概率,理论上的解释为服务端定期有一定的概率调用gc函数来对session进行清理,清理的概率为:gc_probability/gc_divisor 比如:1/100? 表示每一个新会话初始化时,有1%的概率会启动垃圾回收程序,清理的标准为session.gc_maxlifetime定义的时间。
?
[客户端]
session.use_cookies = 1
sessionid在客户端采用的存储方式,置1代表使用cookie记录客户端的sessionid,同时,$_COOKIE变量里才会有$_COOKIE[‘PHPSESSIONID’]这个元素存在;
?
session.use_only_cookies = 1
也是定义sessionid在客户端采用的存储方式,置1代表仅仅使用 cookie 来存放会话 ID。一般来说,现在客户端都会支持cookie,所以建议设置成1,这样可以防止有关通过 URL 传递会话 ID 的攻击。
?
session.use_trans_sid = 0
相对应于上面那个设置,这里如果置1,则代表允许sessionid通过url参数传递,同理,建议设置成0;
?
session.referer_check =?
这个设置在session.use_trans_sid = 1的时候才会生效,目的是检查HTTP头中的"Referer"以判断包含于URL中的会话id是否有效,HTTP_REFERER必须包含这个参数指定的字符串,否则URL中的会话id将被视为无效。所以一般默认为空,即不检查。?
?
session.name = PHPSESSID
定义sessionid的名称,即变量名,通过浏览器http工具可以查看PHPSESSID的值;
?
session.hash_function = 0
选择session_name的加密方式,0代表md5加密,1代表sha1加密,默认是0,但是据说用sha1方式加密,安全性更高;
?
session.hash_bits_per_character = 4
指定在session_name字符串中的每个字符内保存多少位二进制数,这些二进制数是hash函数的运算结果。
4?? bits:?? 0-9,?? a-f?
5?? bits:?? 0-9,?? a-v?
6?? bits:?? 0-9,?? a-z,?? A-Z,?? "-",?? ","
?
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=,fieldset="
指定重写哪些HTML标签来包含sid(session_id)(仅在"session.use_trans_sid"打开的情况下有效),URL重写器将添加一个隐藏的"<input>",它包含了本应当额外追加到URL上的信息。?
?
session.cookie_lifetime = 0
保存sessionid的cookie文件的生命周期,如置0,代表会话结束,则sessionid就自动消失,常见的强行关闭浏览器,就会丢失上一次的sessionid;
?
session.cookie_path = /
保存sessionid的cookie文件在客户端的位置;
?
session.cookie_domain = /
保存sessionid的cookie的域名设置,这跟cookie允许的域名的访问权限设置有关,一般来说想让自己网站所有的目录都能访问到客户端的cookie,就应该设置成“/”如需要详细了解,可以看下setcookie()函数的domain参数相关设置和使用方法;
?
session.bug_compat_42 = 1
session.bug_compat_warn = 1
这两个可以说几乎是快要被废弃的设置,是为了老版本的php服务的,主要是针对session_register函数,因为php5的register_global默认是关闭状态,所以在php5里根本用不到session_register这个函数;并且php6就要废除这个设置,直接定义为关闭,所以没必要研究这两个了;


session_start()做了些什么?

假设php.ini中session的几个关键参数配置为:
session.save_handler = files
session.use_cookies = 1
session.name = PHPSESSID
session.save_path = "/tmp/"

?

下面通过代码样例阐述,在一个会话过程中session_start的作用。

程序1:
<?php
session_start();
$_SESSION['uname'] = 'monkey';
$_SESSION['ukey'] = 20119999;
?>


程序1执行后,session_s