配置Session和存储控制器

3.4 版本
维护中的版本

本节应对的是“如何配置session管理”以及“如何根据需求精细控制它”。文档中涉及了save handlers(存储控制器),它是用来存储和取出session数据并且控制session行为的。

Save Handlers/存储控制器 

PHP的session工作流有6种可能的操作会发生。通常的session遵循openreadwriteclose,另有destroygc可用(垃圾回将令旧session失效:gc将根据PHP的配置情况而随机调用,如果被调用,则是在open操作之后进行)。要了解更多可参考 php.net/session.customhandler

原生PHP存储控制器 

所谓原生handler,是指那些被编译为PHP或由PHP扩展(extension)所提供的,比如PHP-Sqlite、PHP-Memcached之类的handler。

所有原生的save handler都归于PHP内部且无对外API。它们都要由php.ini中的指令来配置,通常是session.save_path,以及其他可能的driver之专属指令。具体细节可以在每个类的setOptions()方法上面的文档注释中找到。例如,Memcached extension所提供的(那个save handler)可以在这里找到php.net/memcached.setoption

虽然原生save handlers可以通过ini_set('session.save_handler', $name);直接激活,Symfony也提供了一个便利方法以相同的方式来激活它们,这是为自定义handlers而准备的。

Symfony对以下原生save handler提供驱动,可作为样例:

例程用法:

1
2
3
4
5
6
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
 
$storage = new NativeSessionStorage(array(), new NativeFileSessionHandler());
$session = new Session($storage);

除去files handler这个内置于PHP并且始终能用的控制器之外,其他(原生)控制器的可用性要依赖于那些PHP扩展(PHP extension)是否在(服务器)运行时被激活。

原生save handler对session存储提供快速解决方案。但是,在你需要更多控制权的复杂系统中,自定义save handler能够提供更多自由度和灵活性。Symfony提供了几种实现,供你进一步按需定制。

自定义存储控制器 

自定义控制器,指的是那些完全取代PHP内建的session save handlers,它们在session工作流中的不同阶段,提供6种callback function(回调函数)供PHP内部调用。

Symfony HttpFoundation组件默认提供了一些(自定义控制器),若你需要编写自己的handler,它们可以做为样例方便你使用。

例程用法:

1
2
3
4
5
6
7
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
 
$pdo = new \PDO(...);
$storage = new NativeSessionStorage(array(), new PdoSessionHandler($pdo));
$session = new Session($storage);

配置PHP Sessions 

NativeSessionStorage可以配置php.ini配置文件中的多数指令,相关指令的文档可参考 php.net/session.configuration

配置这些设定时,把键(keys,忽略原本指令中键里面的session.部分)作为一个“键值”数组传入到$options构造参数中。或者通过 setOptions()方法来设置它们。

为了令文档清晰,一些键选项将在下文进行解释。

Session Cookie Lifetime 

出于安全,session tokens一般被推荐为以session cookies来发送。你可以配置session cookie的生命周期,通过在NativeSessionStorage的构造器参数$options中使用cookie_lifetime这个的键来指定lifetime(秒数)即可。

cookie_lifetime设置为0,将导致cookie仅在浏览器保持开启的过程中有效。总体上,cookie_lifetime可以设置成一个相对大一点的天、周或者是月。根据程序不同,将cookie(的周期)设为一年并不鲜见。

由于session cookies只是个客户端token,它们在你的安全细节控制中不是那么重要,因为相关的控制最终是在服务器端“完全地”完成。

cookie_lifetime选项以秒为单位来描述cookie生存时间,它并非unix时间戳。最终的session cookie将以time() + cookie_lifetime打上过期时间,这个时间来取自服务器。

配置垃圾收集(Garbage Collection) 

当打开一个session之后,PHP会 根据session.gc_probability / session.gc_divisor所设定的probability(概率)来随机调用gc handler。例如,若其各自都被设为5/100,这时的概率就是5%。类似的,3/4意味着有四次中有三次会被调用,即75%。

如果垃圾回收控制器被调用,PHP将传入在php.ini中的session.gc_maxlifetime指令所对应的值。这表示,当前任何已存储的session只要超过了gc_maxlifetime所设置的时间都将被删除。它允许你基于系统空闲来令记录失效。

你可以配置这些设定,把gc_probablitygc_divisorgc_maxlifetime装入数组并传入NativeSessionStorage的构造器,或者使用其setOptions()方法。

Session Lifetime 

当一个新的session被创建时,意味着Symfony要分配一个全新session cookie到客户端了,cookie会被打上“过期时间戳”。这种计算,是把PHP环境配置中session.cookie_lifetime的值,基于当前服务器时间给添加进来。

PHP只派发一次cookie。客户端被预期要在整个lifetime内存储这个cookie。新cookie的派发仅在session被销毁之后(destroyed)、浏览器中的cookie被清除之后、或者通过Session类的migrate()invalidate()方法重新生成session ID之后。

cookie的初始lifetime生命周期,可以通过NativeSessionStorage类中的setOptions(array('cookie_lifetime' => 1234))方法再设置。

cookie的lifetime为0,表示当浏览器关闭时cookie即失效。

Session Idle Time/Keep Alive 

有几种常见情形,你可能希望对一个session的“未经授权”的使用进行保护或最小化——当一个用户从他的终端前起身离开,一定空闲时间之后,session被销毁导致重新登陆。举例来说,这在银行程序中是很常见的,每5到10分钟的不活动都会令用户退出。设置cookie的生命周期在这里是不合适的,因为那是能被客户端操作的,所以我们必须从服务器端来令其失效。最简单的办法是通过令垃圾回收机制“有理由的频繁运行”来实现。可将cookie_lifetime设定在一个相对高的值,而垃圾回收的gc_maxlifetime则设定为“在希望的空闲时间段达成之后”销毁session。

另一个选择,是对启动后的session进行特殊检查,看其是否已过期。如果需要可以销毁session。这种处理办法可以把session过期(这件事)整合到用户体验中,比如,显示一条信息。

Symfony记录了一些关乎session的基础元数据,给了你控制session的完整自由度。

Session Cache Limiting 

为避免用户看到陈旧信息,对于开启了session的资源来说,发送头信息禁止其缓存是较为常见的。为了达到这个目的,PHP Sessions有一个sessions.cache_limiter选项,来决定何种头(如果有的话)将在session启动时和响应一起发出。

在构造时,NativeSessionStorage设置了全局选项为""(不发送头),这是用户希望使用Response对象来管理响应头。

如果你依赖PHP Session来管理HTTP缓存,你必须 手动设置NativeSessionStorage中的cache_limiter选项为“非空值”(non-empty)。

例如,你可以在构造时把它设为PHP默认值。

例程用法:

1
2
3
4
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
 
$options['cache_limiter'] = session_cache_limiter();
$storage = new NativeSessionStorage($options);

Session Metadata(元数据) 

Session可以被一些基础元数据(basic metadata)进行装饰,以便能够精细控制选项。session对象有一个用于元数据的getter,getMetadataBag()暴露(exposes)的是一个MetadataBag实例。

1
2
$session->getMetadataBag()->getCreated();
$session->getMetadataBag()->getLastUsed();

两种方法均返回一个Unix timestamp(相对于服务器时间的时间戳)。

1
2
3
4
5
$session->start();
if (time() - $session->getMetadataBag()->getLastUsed() > $maxIdleTime) {
    $session->invalidate();
    throw new SessionExpired(); // redirect to expired session page 重定向到过期session页
}

针对某个特定cookie,还有一种可能是,告之其cookie_lifetime被设为多少。读取getLifetime()方法:

1
$session->getMetadataBag()->getLifetime();

cookie的过期时间,可以通过其“创建时间”以及“生命周期”来决定。

兼容PHP 5.4 

从PHP 5.40起,SessionHandlerSessionHandlerInterface可以使用了。Symfony为SessionHandlerInterface提供了向下兼容,因此它可以用在PHP 5.3下。这极大地改善了同其他类库的互动性。

SessionHandler是一个特殊的PHP内部类,对user-space暴露的是原生save handler。

为了给使用PHP 5.4的人提供解决方案,Symfonye有一个特殊的类叫NativeSessionHandler,它在PHP 5.4下继承(extends)了\SessionHandler,而在PHP 5.3中只是一个空的基类。这可以在可行时,提供一些很好的机会来利用PHP 5.4的功能性。

Save Handler Proxy 

Save Handler Proxy一般是指将Save Handler打包,能够支持“从PHP 5.3到PHP 5.4+”的无缝升级。它进一步创建了一个extension point(扩展点),可向其中添加自定义逻辑并独立工作,handler在(custom logic中)里面被打包。

save handler的类代理(class proxy)有两种,继承自AbstractProxy:它们是NativeProxySessionHandlerProxy

NatvieSessionStorage自动注入storage handlers到save handler proxy中,除非已经打包好一个。

当PHP内部的save handler被指定使用Native*SessionHandler时,NativeProxy在PHP 5.3下将自动启用,而SessionHandlerProxy将用于打包各种自定义save handler,它们实现的都是SessionHandlerInterface接口。

PHP 5.4和以上版本,所有的session handlers都必须实现SessionHandlerInterface接口,包括继承自SessionHandlerNative*SessionHandler类们。

代理架构(proxy mechanism)能够让你更深层地介入session save handler类。例如,一个代理,可以用于加密任何session处理,而毋需特定的save handler之相关知识。

在PHP 5.4之前,你只能代理user-land save handlers,而无法代理PHP的原生save handlers。

了解更多

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)