PHP会话,Session机制

什么是SESSION?

按照WIKI的解释,SESSION是存在于两个通信设备间的交互信息,在某一时间建立,经过一定的时间后失效。常见的SESSION有:TCP SESSION、WEB SESSION(HTTP SESSION)、LOGIN SESSION等。

根据OSI模型中,会话实现的位置不同,SESSION主要分为几种,一种是应用层会话,包括WEB SESSION(HTTP SESSION)和telnet远程登录session;会话层实现的,包括Session Initiation Protocol(SIP)和Internet Phone Call;在传输层实现的有TCP SESSION。

本文主要讨论WEB SESSION,其一般有两种:客户端SESSION和服务器端SESSION,后一种最常见的属于Java Beans提供的。

SESSION是做什么的?

在计算机领域,特别是网络方面,SESSION使用的特别广泛,也可以称为是对话(Dialogue)、会话等,一般是指在两个通信设备间存储的状态,有时也发生在用户和计算机之间(Login SESSION)。

区别于无状态的通信,SESSION通常用来存储通信状态,因此通信的双方至少有一方需要存储SESSION的历史记录,从而实现两者间的通信。

SESSION(WEB SESSION)是怎么实现的?

浏览器和服务器之间进行HTTP通信时,通常会包含一个 HTTP Cookie 来标识状态,通常会有一个唯一的 SESSIONID ,SESSION通常记录着用户的一些验证信息和级别。

在几中编程语言中最常用的Http Session Token是,JSESSIONID(JSP),PHPSESSID(PHP),ASPSESSIONID(ASP),这个标识通常由哈希函数产生,能够唯一表示这个用户的身份,在服务器和客户端通信时,作为GET或者POST的参数存储在客户端。

SESSION的实现方式通常有两种,服务器端SESSION和客户端SESSION,两种方式各有优缺点。

服务器端SESSION实现容易并且效率比较高,但是遇到负载均衡或者高可用性需求的时候,处理起来就比较困难,对于那种内生系统不存在存储设备的时候,也是不可用的。负载均衡可以通过共享文件系统或者强制客户只能登录到一台服务器上来实现,但是这样会降低效率。对于没有存储的设备,也可以通过使用 RAM(参考参考资料6)来解决服务器端SESSION的实现,这种方法这对哪些客户端链接有限的系统有效(诸如路由或者接入点设备)。

客户端SESSION的使用可以解决服务器端SESSION的一些问题,比如避免了负载均衡的算法等,但是同时也会产生一些自身的问题。客户端SESSION 使用Cookie和加密技术来在不同的请求间保存状态。在每一个动态页面结束后,会统计当前的SESSION,并把它发回客户端。每次成功请求后,会把 cookie再发送到服务器端,来让服务器“记起”这个用户的身份。客户端SESSION最重要的问题就是安全问题,一旦cookie被劫持或者篡改了,用户的信息的安全性就丧失了。

PHP中如何设置SESSION?

搭建好PHP的开发环境后,通过phpinfo()可以查看到与SESSION有关的部分包括:

SESSION模块,在PHP V5.2.9版本中,一共有25个变量。其中,平时设置中常会用到的几个有:

session.cookie_lifetime 设置存储SESSIONID的cookie过期时间

session.name SESSION的COOKIE名称,默认为PHPSESSID

session.save_handler SESSION的存储方式,默认为FILE

session.save_path Fedora下面默认存储在/var/lib/php/session(用户可以自己设置如win下 “C:\php\session”)

session.gc_probability 默认为1

session.gc_divisor 默认为1000

session.gc_maxlifetime 这三个选项用来处理垃圾回收机制GC发生的机率,计算的公式是session.gc_probability/session.gc_divisor,这代表每一次Session的初始化都有1/1000的机会启动GC机制,启动后GC会逐个根据session.gc_maxlifetime检查Session文件是否过期,过期即删除。

session.cache_limiter (nocache,private,private_no_expire,public)

session.cache_expire 这两个选项是用来缓存SESSION的页面

先来考虑第一个问题,SESSION多久会过期,他是如何过期的?如果要在PHP程序中使用SESSION,一定要先引用session_start(),这个函数一执行,就会在SESSION的存储目录(如果使用了file handler)生成一个SESSION文件,里面内容是空的,同时浏览器会见里一个name为PHPSESSID的cookie,里面存储着一个 hash出来的SESSION的名字。

SESSION的过期依赖于一个垃圾回收机制(Garbage Collection),SESSION创建后作为一个文件存放在服务器上,客户端脚本每访问一次SESSION中的变量,SESSION文件的访问时间就会进行更新。每次访问都是根据客户端存储的SESSIONID去请求服务器中存储的唯一的SESSION,当客户端的cookie过期后,就无法知道要访问的是哪一个SESSION,尽管此时服务器上的SESSION文件还没有被过期收回,这样就会造成服务器资源的浪费。

但是同时,如果我们希望用户的session马上过期的话,我们就可以通过设置cookie的办法来实现。SESSION的回收是在每次访问页面的时候进行的,回收的机率由 session.gc_probability,session_gc_divisor指定,默认士1/100。如果设置为1,则每次超过了 SESSION的生存周期去访问的话,SESSION一定会被回收。

对Session的概念有了大概的认识,下面简单举例说明在做项目的时候经常用到的一些Session设置(这里的Session通过Cookie实现)。

在做一个登录站点并想保持用户状态的时候,可能会涉及到下面几种情况:

1>用户登录浏览页面, 直到关闭浏览器会话信息才消失

设置php.ini,找到 session.cookie_lifetime = 0 设置为0

2>用户登录,1个小时不做任何操作,会话信息消失(用户需要重新登录),如果操作则更新会话信息

设置php.ini,设置 session.cookie_lifetime = 3600

登录之后这样设置Session:

1 <?php 
2 session_register('u_id');
3 $_SESSION['u_id'] = 12;
4 ?>

或者不想或者无权修改php.ini

1 <?php 
2 session_register('u_id');
3 $_SESSION['u_id'] = 12;
4 setcookie(ini_get('session.name'),session_id(),time() + 3600);
5 ?>

更新Session保持存活状态,这样可以使用户在不断访问的情况下,不被强制退出

1 if (isset($_SESSION['u_id'])) {
2   setcookie(ini_get('session.name'),session_id(),time() + 3600);//如果用户处于登录状态,重新计算1小时过期时间
3 }else {
4   //跳转登录页面
5 }

在做这些设置之前一定要保证服务器端的Session没有过期,比如session.gc_maxlifetime = 1440(24分钟),那么超过24分钟的Session每次用户访问都有1/1000的几率被删除,特别是多站点共用一台服务器,更容易被删除,因为GC是公共的。

3>用户登录,不论是否关闭浏览器,是否操作,3天内保持用户登录状态

这种和第二种我设置原理一样 setcookie(ini_get(‘session.name’),session_id(),time() + $expire); //$expire 修改为3*24*60*60

4>如果session.gc_maxlifetime 设置过大,服务器上会存储大量的Session文件,怎么办?(仅供参考)

a. session.gc_maxlifetime 和session.cookie_lifetime 值设置小点,在需要长时间等待的页面上用ajax没隔一段时间去请求一次服务器,保证Session不过期。

b.如果一台服务器上有多个站点,每个站点设置不同的Session保存目录

SESSION安全吗?

PHP的手册中明确写出:SESSION并不能保证储存在SESSION中的信息一定只能被他的创建者所看到。

如果想要安全的处理一些远程的操作,那么HTTPS是唯一的选择。最基本的,不要认为一个用户信息在SESSION中存在就认为这个用户一定就是他本人,虽然SESSION中的信息会给你他已经经过了用户名和密码验证的假象。所以,如果需要做一些修改密码或者类似的事情的时候,让用户重新输入密码是一个比较好的选择。

早期的Apache版本并没有采用COOKIE的方式来存储PHPSESSID,而是采用的URL-rewrite,也就是每个 URL后面都会加上PHPSESSID=来表明它属于那个激活的SESSION,新版的Apache已经将这个属性设置为默认关闭。

session.use_trans_id = 0;

所以从这个意义上来讲,延长SESSION的时间过长或者保持SESSION一直在线对于安全来说始终不是一件好事情。终极的解决办法就是用户提交跳转到登录窗口,登录后又能够回到填写页面,并且所有的数据都还在。这个的实现方式现在用Ajax来解决应该没什么困难,每隔一定时间就把当前的用户数据POST到一个存储位置,不管是XML或者JSON。