Session相关总结

目录

前言

  本篇简单总结session的概念,作用,应用,实现案例等要点,关于大段的介绍,就不再重复了,网上有很多可以参考。而且session相关是一个很大的话题,这里只是简单小结,主要包括以下几个大的方面:
  1.直观感受
  2.session相关介绍
  3.session安全性问题
  4.session的不同实现方式
  5.cookie与session的对比分析
  6.session在PHP开发中的使用


直观感受

使用session进行状态保持的常用方法是服务端生成session对象的同时,在http响应报文首部通过set-cookie字段设置一个比如叫“PHPSESSID”的cookie以此进行唯一标识(但注意这不是唯一方法),如下图所示:

当用户再次发起请求时,http请求报文首部携带这个cookie,用于服务端验证,如下图所示:


session相关介绍

定义

Session 允许通过将对象存储在 Web服务器的内存中在整个用户会话过程中保持任何对象。即“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求发起时,如果该用户还没有会话,则Web服务器将自动创建一个Session对象。当会话过期或被放弃后,服务器将终止该会话。Session对象最常见的一个用法就是存储用户的首选项。注意,会话状态仅在支持cookie的浏览器中保留。(这个的原因在“cookie相关总结”一文中cookie与session的对比分析中已经提到。)

  需要明确的是,与cookie相比,Session是用于保持状态的基于服务端的解决方案。Session的工作机制是:为每个访客创建一个唯一的id(UID,名称中一般有类似“SESSID”字样),并基于这个UID来存储变量。UID或者存储在cookie中(在cookie的协助下共同完成会话状态保持),或者通过URL进行传递(URL重写)。也就是说,session方案实现不是一定需要有cookie的协助,反之亦然,两者是独立的。这样,服务器端在接到请求时候,就会收session ID,并根据ID在内存中找到之前创建的session对象,提供给请求使用。

要求

  session通常用于保存客户端的状态信息,这些信息需要保存在服务器的硬盘上,所以要求session的属性值必须是可序列化的,否则将会引发不可序列化的异常。

session的生成时间

典型的错误认识:session在客户端访问时就会被创建。

  事实上是直到某服务端程序显式调用session相关操作时才会被创建,比如在Java中调用HttpServletRequest.getSession(true);,或者使用ThinkPHP框架调用封装好的函数session(array('name'=>'session_id','expire'=>3600));,再或者原生PHP中调用session_start();的时候。并且,访问静态资源时是不会创建session的。

session的存储方式

  以PHP为例,session的存储方式主要有3中:文件存储、内存存储、自定义存储。默认是使用文件存储。具体配置方式如下:
D:\code\WampServer\wamp64\bin\apache\apache2.4.17\bin目录下有php.ini文件,打开该文件,可找到如下片段:

session.save_handler = files表明使用文件存储方式;
session.save_path ="D:/code/WampServer/wamp64/tmp"则表明session文件的存储路径

主要就是需要配置这两个变量,这里都是默认配置。

于是,访问网站登陆后生成了自己的session,在此目录下,我们就可以看到一系列前缀为”sess_”的文件,这就是不同的session创建的对应文件:

说明:
关于这个php.ini文件,目前还有一些不太理解的地方:
在Apache的bin目录下D:\code\WampServer\wamp64\bin\apache\apache2.4.17\bin
有php.ini文件,而在PHP自身的目录下D:\code\WampServer\wamp64\bin\php\php5.6.16
也有这个php.ini文件(以及php.ini-production php.ini-development文件)

那么如果要配置一些东西,到底是在哪个文件中配置生效呢?
虽然结论是可知的,通过phpinfo可以看到如下信息:

说明是Apache的bin目录下D:\code\WampServer\wamp64\bin\apache\apache2.4.17\bin这个文件生效,但是这个Loaded Configuration File是从哪里配置的呢?还没有搞清楚。。。只是从php.ini文件的官方注释中获取了如下一些信息–关于PHP加载时寻找该文件的优先级排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
;;;;;;;;;;;;;;;;;;;
; About php.ini ;
;;;;;;;;;;;;;;;;;;;
; PHP's initialization file, generally called php.ini, is responsible for
; configuring many of the aspects of PHP's behavior.

; PHP attempts to find and load this configuration from a number of locations.
; The following is a summary of its search order:
; 1. SAPI module specific location.
; 2. The PHPRC environment variable. (As of PHP 5.2.0)
; 3. A number of predefined registry keys on Windows (As of PHP 5.2.0)
; 4. Current working directory (except CLI)
; 5. The web server's directory (for SAPI modules), or directory of PHP
; (otherwise in Windows)
; 6. The directory from the --with-config-file-path compile time option, or the
; Windows directory (C:\windows or C:\winnt)
; See the PHP docs for more specific information.
; http://php.net/configuration.file

普遍的说法是在Apache下conf目录下的httpd.conf文件中配置PHPIniDir,但我看了这个文件里面根本没有PHPIniDir。。。

另外,如果是搭建LAMP环境,使用了源码编译安装PHP的方式的话,可以用以下参数指定php.ini文件的位置,也就没有这个歧义了:
--with-config-file-path=/usr/local/php/etc
即使用类似如下命令:
# ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc

session的销毁时间

典型的错误认识:只要关闭浏览器,session就消失了。

  其实不然,除非程序主动通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做“退出当前账户”的时候发出指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存SESSID,而这种cookie是非持久的,保存在内存中,关闭浏览器后这个SESSID就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的SESSID发送给服务器,则再次打开浏览器仍然能够找到原来的session。恰恰是由于关闭浏览器不会导致session被删除,使得服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

  具体的session删除更新机制还有些复杂,不像上面说的时间一过就删除这么简单。关于session的销毁,还有一些问题—session销毁的“可能性”及垃圾回收:

  在上文提到的D:\code\WampServer\wamp64\bin\apache\apache2.4.17\bin目录下的php.ini文件中,有这样两个配置项:

大致的机制是这样的:
由上述内容已经知道,session是有过期时间的,过期后将会被删除。但对于大型网站来讲,访问用户量巨大,为每个用户创建session是肯定的,但在如果过期时间一到,就删除那些过期session,从概率分布角度来说,服务端一定是经常要删除新一批到期的session。这无疑大大增加了系统的IO开销。因此,过期session是以一定概率进行垃圾回收的,这一定程度上保证了系统性能。具体来说,这个概率定义了每次新session初始化时启动垃圾回收进程的可能性。计算公式就是由上图中两个量相除得到:
GC启动可能性 = gc_probability/gc_divisor
比如说,gc_probability = 1,gc_divisor = 100,那么每次进行有访问请求后时,就有1%的可能性启动GC进程。而这个进程一旦启动,就会扫描指定的session临时文件夹,将过期的session文件删除。

  但是,这里还是有一些问题,做如下操作:
1.清空tmp文件夹,清空网站cookie,准备干净的环境进行对比。
2.同时打开两个浏览器,模拟不同客户端的登录行为(这里不用一个浏览器的不同窗口,因为对不同的浏览器,不同窗口是否共用session好像没有统一的结论)
3.写一个最简单的页面,比如就输出“123”,我们就是为了研究session现象,排除干扰。(对应MVC中的V)
4.该页面对应的C也就是controller,只做两件事:启动session,渲染页面(关闭框架配置中的session自启动等,只用原生代码手动开启session)
5.修改配置文件:设置

1
2
gc_probability = 1;
gc_divisor = 1;

让两者相等,使得GC启动可能性为1,确保一定会对过期session做操作,以此来研究session的变化。

6.在后台代码配置文件中声明:

1
2
3
4
//session配置
'SESSION_OPTIONS' => array(
'expire' => 10,
),

设置session过期时间为10秒,即通过ThinkPHP框架配置文件的方式覆盖php.ini文件中的session.gc_maxlifetime = 1440(也可以直接在php.ini文件中修改)

探究—PHP中session的垃圾回收(GC)机制

session的生存周期

以上描述的session创建,存储,修改,销毁以及其中各种细节,就是session完整的生命周期了。


session安全性问题


cookie与session的对比分析

  这一部分在cookie相关总结中已经提过。


session在PHP开发中的使用

  实际开发中使用了ThinkPHP框架,框架提供了Session管理和操作的完善支持,全部操作可以通过一个内置的session函数完成,该函数可以完成Session的设置、获取、删除和管理操作。Session初始化设置方法无需手动调用,在Think\App类的初始化工作结束后会自动调用,通常项目只需要配置SESSION_OPTIONS参数即可,SESSION_OPTIONS参数的设置是一个数组,支持的索引名和前面的session初始化参数相同。默认情况下,初始化之后系统会自动启动session,如果不希望系统自动启动session的话,可以设置SESSION_AUTO_START为false。
  具体的:
1.配置文件声明
2.根据开发惯例结合ThinkPHP的写法,在MVC结构中,“C”即controller层应该有一个共用的基础controller即BaseController。考虑到用户会话保持是一个在访问整个网站中任意页面都需要的功能,因此session创建相关的内容非常合适(也只应该)放在BaseController中来完成。也就是说,BaseController应该具有全局(静态)方法is_login()用来判断用户是否登录,从而相应创建session,这样如果用户直接访问网站任意的子目录,由于继承了BaseController,都会做验证,保证了安全性。
  但是,有一处比较特殊要注意,用户登录鉴权方法所在的controller(比如IndexController)是无法继承BaseController的。