buimatic / session
基于 aura/session
Requires
- php: >=5.3.0
Requires (Dev)
- aura/di: ~2.0
Suggests
- ext-mcrypt: Mcrypt generates the next best secure CSRF tokens.
- ext-openssl: OpenSSL generates the best secure CSRF tokens.
- ircmaxell/random-lib: A Library For Generating Secure Random Numbers
This package is not auto-updated.
Last update: 2024-10-02 09:36:35 UTC
README
提供会话管理功能,包括懒加载会话启动、会话段、仅下次请求(“闪光”)值和CSRF工具。
前言
安装
此库需要PHP 5.3或更高版本;我们原则上建议使用最新版本的PHP。它没有用户空间依赖。
您可以通过Composer安装和自动加载,地址为 aura/session。
或者,您可以 下载一个发布版 或克隆此存储库,然后要求或包含其 autoload.php 文件。
质量
要在命令行运行单元测试,请执行 composer install
,然后在包根目录下执行 phpunit
。这需要 Composer 以 composer
的形式可用,以及 PHPUnit 以 phpunit
的形式可用。
此库试图遵守 PSR-1、PSR-2 和 PSR-4。如果您注意到遵守上的疏忽,请通过拉取请求发送补丁。
社区
要提问、提供反馈或与Aura社区进行其他沟通,请加入我们的 Google Group,关注 @auraphp on Twitter 或在Freenode上的 #auraphp 上与我们聊天。
入门
实例化
开始的最简单方法是使用 SessionFactory 创建一个 Session 管理对象。
<?php $session_factory = new \Aura\Session\SessionFactory; $session = $session_factory->newInstance($_COOKIE); ?>
然后我们可以使用 Session 实例创建 Segment 对象来管理会话值和闪存。 (通常,我们不需要直接操作 Session 管理器 - 我们将主要与 Segment 对象一起工作。)
段
在正常的PHP中,我们将会话值保存在 $_SESSION
数组中。然而,当不同的库和项目尝试修改相同的键时,产生的冲突可能导致意外的行为。为了解决这个问题,我们使用 Segment 对象。每个 Segment 针对在 $_SESSION
数组中的命名键进行去冲突。
例如,如果我们为 Vendor\Package\ClassName
获取一个 Segment,那么该 Segment 将包含对 $_SESSION['Vendor\Package\ClassName']
的引用。然后我们可以在 Segment 上 set()
和 get()
值,并且值将驻留在该引用下的数组中。
<?php // get a _Segment_ object $segment = $session->getSegment('Vendor\Package\ClassName'); // try to get a value from the segment; // if it does not exist, return an alternative value echo $segment->get('foo'); // null echo $segment->get('baz', 'not set'); // 'not set' // set some values on the segment $segment->set('foo', 'bar'); $segment->set('baz', 'dib'); // the $_SESSION array is now: // $_SESSION = array( // 'Vendor\Package\ClassName' => array( // 'foo' => 'bar', // 'baz' => 'dib', // ), // ); // try again to get a value from the segment echo $segment->get('foo'); // 'bar' // because the segment is a reference to $_SESSION, we can modify // the superglobal directly and the segment values will also change $_SESSION['Vendor\Package\ClassName']['zim'] = 'gir' echo $segment->get('zim'); // 'gir' ?>
会话段的好处是我们可以通过使用类名(或某些其他唯一名称)作为段名称来去冲突 $_SESSION
超全局变量中的键。使用段,不同的包可以在不互相干扰的情况下使用 $_SESSION
超全局变量。
要清除 Segment 上的所有值,请使用 clear()
方法。
闪光值
段值会持续到会话被清除或销毁。然而,有时设置一个只通过下一个请求传播并在之后丢弃的值是有用的。这些被称为“闪存”值。
设置和获取闪存值
要在段上设置闪存值,请使用setFlash()
方法。
<?php $segment = $session->getSegment('Vendor\Package\ClassName'); $segment->setFlash('message', 'Hello world!'); ?>
然后,在后续请求中,我们可以使用getFlash()
读取闪存值。
<?php $segment = $session->getSegment('Vendor\Package\ClassName'); $message = $segment->getFlash('message'); // 'Hello world!' ?>
注意:与
get()
一样,如果闪存键不存在,我们可以提供一个备选值。例如,getFlash('foo', 'not set')
如果不存在'foo'键,将返回'not set'。
使用setFlash()
只使闪存值在下一个请求中可用,而不是当前请求。要使闪存值在当前请求和下一个请求中都立即可用,请使用setFlashNow($key, $val)
。
使用getFlash()
仅返回从前一个请求设置的当前可用的值。要读取将在下一个请求中可用的值,请使用getFlashNext($key, $alt)
。
保留和清除闪存值
有时我们希望将闪存值保留在当前请求中以便用于下一个请求。我们可以通过调用Segment的keepFlash()
方法按段进行,或者通过调用Session的keepFlash()
方法保留所有段的所有闪存。
同样,我们可以按段或会话范围清除闪存值。在Segment上使用clearFlash()
方法清除仅针对该段的闪存,或在Session上使用相同的方法清除所有段的所有闪存值。
延迟会话启动
仅实例化Session管理器并从中获取Segment并不会调用session_start()
。相反,session_start()
只在某些情况下发生。
-
如果我们从Segment中读取(例如使用
get()
),Session会检查是否已经设置了会话cookie。如果是这样,它将调用session_start()
来恢复先前启动的会话。如果没有,它知道没有先前存在的$_SESSION
值,因此不会调用session_start()
。 -
如果我们向Segment写入(例如使用
set()
),Session将始终调用session_start()
。这将恢复现有会话,如果存在的话,或者如果没有,将启动一个新的会话。
这意味着我们可以随意创建每个Segment,而session_start()
将不会在实际上以特定方式与Segment交互之前被调用。这有助于节省启动会话所需的资源。
当然,我们可以通过调用Session的start()
方法强制会话启动或重新激活,但这会违背延迟加载会话的目的。
保存、清除和销毁会话
注意:这些方法适用于所有段的所有会话数据。
要保存会话数据并在当前请求中结束其使用,请调用Session管理器的commit()
方法。
<?php $session->commit(); ?>
注意:根据https://php.ac.cn/manual/en/session.examples.basic.php,“会话通常在PHP完成执行脚本时自动关闭,但可以使用session_write_close()函数手动关闭。”
commit()
方法与session_write_close()
等价。
要清除所有会话数据,但在当前请求中保持会话活动,请使用Session管理器的clear()
方法。
<?php $session->clear(); ?>
要清除段上的所有闪存值,请使用clearFlash()
方法。
要清除数据和终止当前请求和未来请求的会话,从而完全销毁它,请调用destroy()
方法。
<?php $session->destroy(); // equivalent of session_destroy() ?>
调用 destroy()
也会通过 setcookie()
删除会话cookie。如果我们有其他删除cookie的方法,我们应该将一个可调用对象作为第二个参数传递给 SessionFactory 方法的 newInstance()
。该可调用对象应接受三个参数:cookie名称、路径和域名。
<?php // assume $response is a framework response object. // this will be used to delete the session cookie. $delete_cookie = function ($name, $path, $domain) use ($response) { $response->cookies->delete($name, $path, $domain); } $session = $session_factory->newInstance($_COOKIE, $delete_cookie); ?>
会话安全
会话ID重新生成
每当用户权限发生变化(即在系统中获得或失去访问权限)时,请务必重新生成会话ID
<?php $session->regenerateId(); ?>
注意:
regenerateId()
方法也会重新生成CSRF令牌值。
跨站请求伪造
“跨站请求伪造”是一种安全漏洞,攻击者通过恶意JavaScript或其他方式,从客户端浏览器向用户已经认证的服务器发送盲请求。服务器认为请求是有效的,但实际上是伪造的,因为用户并没有真正发起请求(恶意JavaScript做了)。
http://en.wikipedia.org/wiki/Cross-site_request_forgery
防御CSRF攻击
为了防御CSRF攻击,服务器端逻辑应该
-
在每个表单中放置每个已认证用户会话的独特令牌值;并且
-
检查所有传入的POST/PUT/DELETE(即“不安全”)请求是否包含该值。
注意:如果我们的应用程序使用GET请求修改资源(这实际上是不正确的GET使用方式),我们还应该检查来自已认证用户的GET请求上的CSRF。
对于这个例子,表单字段名称将是 __csrf_value
。在每个我们希望保护免受CSRF攻击的表单中,我们使用该字段的会话CSRF令牌值
<?php /** * @var Vendor\Package\User $user A user-authentication object. * @var Aura\Session\Session $session A session management object. */ ?> <form method="post"> <?php if ($user->auth->isValid()) { $csrf_value = $session->getCsrfToken()->getValue(); echo '<input type="hidden" name="__csrf_value" value="' . htmlspecialchars($csrf_value, ENT_QUOTES, 'UTF-8') . '"></input>'; } ?> <!-- other form fields --> </form>
在处理请求时,检查传入的CSRF令牌是否对已认证用户有效
<?php /** * @var Vendor\Package\User $user A user-authentication object. * @var Aura\Session\Session $session A session management object. */ $unsafe = $_SERVER['REQUEST_METHOD'] == 'POST' || $_SERVER['REQUEST_METHOD'] == 'PUT' || $_SERVER['REQUEST_METHOD'] == 'DELETE'; if ($unsafe && $user->auth->isValid()) { $csrf_value = $_POST['__csrf_value']; $csrf_token = $session->getCsrfToken(); if (! $csrf_token->isValid($csrf_value)) { echo "This looks like a cross-site request forgery."; } else { echo "This looks like a valid request."; } } else { echo "CSRF attacks only affect unsafe requests by authenticated users."; } ?>
CSRF值生成
为了CSRF令牌有用,其随机值必须是加密安全的。使用像 mt_rand()
这样的方法是不足够的。Aura.Session附带一个实现 RandvalInterface
的 Randval
类,并使用 openssl
或 mcrypt
扩展来生成随机值。如果您没有安装这些扩展之一,您需要实现自己的 RandvalInterface
随机值实现。我们建议使用RandomLib的包装器。
会话有效期
我们可以使用Session对象的setCookieParams
设置会话有效期,有效期可以是任意长或任意短。有效期以秒为单位。要将会话cookie有效期设置为两周
<?php
$session->setCookieParams(array('lifetime' => '1209600'));
?>
注意:
setCookieParams
方法内部调用session_set_cookie_params。