支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
本节应对的是“如何配置session管理”以及“如何根据需求精细控制它”。文档中涉及了save handlers(存储控制器),它是用来存储和取出session数据并且控制session行为的。
PHP的session工作流有6种可能的操作会发生。通常的session遵循open
,read
,write
和close
,另有destroy
和gc
可用(垃圾回将令旧session失效:gc
将根据PHP的配置情况而随机调用,如果被调用,则是在open
操作之后进行)。要了解更多可参考
php.net/session.customhandler
所谓原生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); |
NativeSessionStorage
可以配置php.ini
配置文件中的多数指令,相关指令的文档可参考
php.net/session.configuration
配置这些设定时,把键(keys,忽略原本指令中键里面的session.
部分)作为一个“键值”数组传入到$options
构造参数中。或者通过
setOptions()
方法来设置它们。
为了令文档清晰,一些键选项将在下文进行解释。
出于安全,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
打上过期时间,这个时间来取自服务器。
当打开一个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_probablity
,gc_divisor
和gc_maxlifetime
装入数组并传入NativeSessionStorage
的构造器,或者使用其setOptions()
方法。
当一个新的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的“未经授权”的使用进行保护或最小化——当一个用户从他的终端前起身离开,一定空闲时间之后,session被销毁导致重新登陆。举例来说,这在银行程序中是很常见的,每5到10分钟的不活动都会令用户退出。设置cookie的生命周期在这里是不合适的,因为那是能被客户端操作的,所以我们必须从服务器端来令其失效。最简单的办法是通过令垃圾回收机制“有理由的频繁运行”来实现。可将cookie_lifetime
设定在一个相对高的值,而垃圾回收的gc_maxlifetime
则设定为“在希望的空闲时间段达成之后”销毁session。
另一个选择,是对启动后的session进行特殊检查,看其是否已过期。如果需要可以销毁session。这种处理办法可以把session过期(这件事)整合到用户体验中,比如,显示一条信息。
Symfony记录了一些关乎session的基础元数据,给了你控制session的完整自由度。
为避免用户看到陈旧信息,对于开启了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可以被一些基础元数据(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.40起,SessionHandler
和SessionHandlerInterface
可以使用了。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打包,能够支持“从PHP 5.3到PHP 5.4+”的无缝升级。它进一步创建了一个extension point(扩展点),可向其中添加自定义逻辑并独立工作,handler在(custom logic中)里面被打包。
save handler的类代理(class proxy)有两种,继承自AbstractProxy
:它们是NativeProxy
和SessionHandlerProxy
。
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
接口,包括继承自SessionHandler
的Native*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 创作共用授权。