CakePHP 在 PHP 原生的 session
扩展之上提供了一个包装器和一套实用程序功能。会话允许您跨请求识别唯一用户并为特定用户存储持久数据。与 Cookie 不同,会话数据在客户端不可用。在 CakePHP 中,通常避免使用 $_SESSION
,而是首选使用 Session 类。
会话配置通常在 /config/app.php 中定义。可用的选项是
Session.timeout
- 会话可以保持“空闲”的分钟数。如果在 timeout
分钟内没有收到请求,CakePHP 的会话处理程序将使会话过期。您可以将此选项设置为 0
来禁用服务器端的空闲超时。
Session.defaults
- 允许您使用内置的默认会话配置作为会话配置的基础。请参阅以下内置默认值。
Session.handler
- 允许您定义自定义会话处理程序。核心数据库和缓存会话处理程序使用它。请参阅以下有关会话处理程序的更多信息。
Session.ini
- 允许您为您的配置设置额外的会话 ini 设置。这与 Session.handler
结合使用,取代了先前版本中的自定义会话处理功能
Session.cookie
- 要使用的 cookie 的名称。默认为 php.ini 配置中设置的 session.name
值。
Session.cookiePath
- 设置会话 cookie 的 URL 路径。映射到 session.cookie_path
php.ini 配置。默认为应用程序的基路径。
CakePHP 在您的应用程序使用 SSL 协议时将默认 session.cookie_secure
设置为 true
。如果您的应用程序同时从 SSL 和非 SSL 协议提供服务,那么您可能会遇到会话丢失的问题。如果您需要在 SSL 和非 SSL 域上访问会话,则需要禁用此选项
Configure::write('Session', [
'defaults' => 'php',
'ini' => [
'session.cookie_secure' => false
]
]);
CakePHP 还默认将 SameSite 属性设置为 Lax
用于会话 cookie,这有助于防止 CSRF 攻击。您可以通过设置 session.cookie_samesite
php.ini 配置来更改默认值
Configure::write('Session', [
'defaults' => 'php',
'ini' => [
'session.cookie_samesite' => 'Strict',
],
]);
会话 cookie 路径默认设置为应用程序的基路径。要更改此设置,您可以使用 session.cookie_path
ini 值。例如,如果您希望会话在所有子域上持久保存,则可以执行以下操作
Configure::write('Session', [
'defaults' => 'php',
'ini' => [
'session.cookie_path' => '/',
'session.cookie_domain' => '.yourdomain.com',
],
]);
默认情况下,PHP 将会话 cookie 设置为在浏览器关闭时立即过期,而不管配置的 Session.timeout
值如何。cookie 超时由 session.cookie_lifetime
ini 值控制,可以使用以下方法进行配置
Configure::write('Session', [
'defaults' => 'php',
'ini' => [
// Invalidate the cookie after 30 minutes
'session.cookie_lifetime' => 1800
]
]);
Session.timeout
与 session.cookie_lifetime
值之间的区别在于,后者依赖于客户端对 cookie 的真实性。如果您需要更严格的超时检查,而不依赖于客户端报告的内容,则应该使用 Session.timeout
。
请注意,Session.timeout
对应于用户不活动的总时间(即没有访问使用会话的任何页面时的时间),并且不限制用户可以在站点上保持活动状态的总分钟数。
CakePHP 附带了一些内置的会话配置。您可以将这些配置用作会话配置的基础,也可以创建完全自定义的解决方案。要使用默认值,只需将‘defaults’键设置为要使用的默认值名称。然后,您可以通过在您的 Session 配置中声明来覆盖任何子设置
Configure::write('Session', [
'defaults' => 'php'
]);
以上将使用内置的‘php’会话配置。您可以通过以下操作来部分或全部增强它
Configure::write('Session', [
'defaults' => 'php',
'cookie' => 'my_app',
'timeout' => 4320 // 3 days
]);
上面为‘php’会话配置覆盖了超时和 cookie 名称。内置配置为
php
- 使用 php.ini 文件中的标准设置保存会话。
cake
- 将会话保存为 tmp/sessions
中的文件。这在主机不允许您在自己的 home dir 之外写入时是一个不错的选择。
database
- 使用内置的数据库会话。请参阅以下更多信息。
cache
- 使用内置的缓存会话。请参阅以下更多信息。
会话处理程序也可以在会话配置数组中定义。通过定义‘handler.engine’配置键,您可以命名类名,或提供一个处理程序实例。该类/对象必须实现本机 PHP SessionHandlerInterface
。实现此接口将允许 Session
自动映射处理程序的方法。核心缓存和数据库会话处理程序都使用此方法来保存会话。处理程序的其他设置应放置在 handler 数组中。然后,您可以在处理程序内部读取这些值
'Session' => [
'handler' => [
'engine' => 'DatabaseSession',
'model' => 'CustomSessions',
],
]
上面显示了如何使用应用程序模型设置数据库会话处理程序。在将类名用作 handler.engine 时,CakePHP 预计在 Http\Session
命名空间中找到您的类。例如,如果您有一个 AppSessionHandler
类,则文件应为 src/Http/Session/AppSessionHandler.php,类名应为 App\Http\Session\AppSessionHandler
。您也可以从插件内部使用会话处理程序。通过将 engine 设置为 MyPlugin.PluginSessionHandler
。
如果您需要使用数据库来存储会话数据,请按如下配置
'Session' => [
'defaults' => 'database'
]
此配置需要一个具有以下架构的数据库表
CREATE TABLE `sessions` (
`id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`created` datetime DEFAULT CURRENT_TIMESTAMP, -- Optional
`modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Optional
`data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
`expires` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
您可以在 应用程序骨架 中的 config/schema/sessions.sql 中找到会话表的架构副本。
您也可以使用自己的 Table
类来处理会话的保存
'Session' => [
'defaults' => 'database',
'handler' => [
'engine' => 'DatabaseSession',
'model' => 'CustomSessions',
],
]
以上将告诉 Session 使用内置的“database”默认值,并指定名为 CustomSessions
的表格将作为委托方将会话信息保存到数据库中。
Cache 类也可以用来存储会话。这允许您将会话存储在诸如 APCu 或 Memcached 之类的缓存中。使用缓存会话有一些注意事项,即如果您耗尽了缓存空间,会话将开始过期,因为记录会被逐出。
要使用基于缓存的会话,您可以配置您的 Session 配置,例如
Configure::write('Session', [
'defaults' => 'cache',
'handler' => [
'config' => 'session',
],
]);
这将配置 Session 使用 CacheSession
类作为委托方来保存会话。您可以使用“config”键来指定要使用的缓存配置。默认的缓存配置是 'default'
。
应用程序框架预先配置了这样的会话配置
'Session' => [
'defaults' => 'php',
],
这意味着 CakePHP 将通过您在 php.ini
中配置的内容来处理会话。在大多数情况下,这将是默认配置,因此 PHP 将任何新创建的会话保存为文件,例如 /var/lib/php/session
但这同时也意味着任何计算量大的任务,例如查询大型数据集并结合使用活动会话,都会 **锁定该会话文件** - 因此阻止用户例如在此时打开应用程序的第二个选项卡来执行其他操作。
要防止这种行为,您需要使用不同的会话处理程序来更改 CakePHP 中处理会话的方式,例如使用 缓存会话 并结合使用 Redis 引擎 或其他缓存引擎。
提示
如果您想了解更多关于会话锁定的信息,请查看 这里
内置的默认值试图提供一个常见的会话配置基础。您可能还需要调整特定的 ini 标志。CakePHP 公开了自定义默认配置和自定义配置的 ini 设置的功能。会话设置中的 ini
键允许您指定单独的配置值。例如,您可以使用它来控制诸如 session.gc_divisor
之类的设置
Configure::write('Session', [
'defaults' => 'php',
'ini' => [
'session.cookie_name' => 'MyCookie',
'session.cookie_lifetime' => 1800, // Valid for 30 minutes
'session.gc_divisor' => 1000,
'session.cookie_httponly' => true
]
]);
在 CakePHP 中创建自定义会话处理程序很简单。在这个例子中,我们将创建一个会话处理程序,它将会话同时存储在缓存(APC)和数据库中。这让我们可以利用 APC 的快速 IO,而不必担心缓存填满时会话会消失。
首先,我们需要创建自定义类并将它放在 src/Http/Session/ComboSession.php 中。该类应该看起来像这样
namespace App\Http\Session;
use Cake\Cache\Cache;
use Cake\Core\Configure;
use Cake\Http\Session\DatabaseSession;
class ComboSession extends DatabaseSession
{
protected $cacheKey;
public function __construct()
{
$this->cacheKey = Configure::read('Session.handler.cache');
parent::__construct();
}
// Read data from the session.
public function read($id): string
{
$result = Cache::read($id, $this->cacheKey);
if ($result) {
return $result;
}
return parent::read($id);
}
// Write data into the session.
public function write($id, $data): bool
{
Cache::write($id, $data, $this->cacheKey);
return parent::write($id, $data);
}
// Destroy a session.
public function destroy($id): bool
{
Cache::delete($id, $this->cacheKey);
return parent::destroy($id);
}
// Removes expired sessions.
public function gc($expires = null): bool
{
return parent::gc($expires);
}
}
我们的类扩展了内置的 DatabaseSession
,因此我们不必复制其所有逻辑和行为。我们用 Cake\Cache\Cache
操作包装每个操作。这让我们可以从快速缓存中获取会话,而不必担心当我们填满缓存时会发生什么。在 config/app.php 中,让会话块看起来像这样
'Session' => [
'defaults' => 'database',
'handler' => [
'engine' => 'ComboSession',
'model' => 'Session',
'cache' => 'apc',
],
],
// Make sure to add a apc cache config
'Cache' => [
'apc' => ['engine' => 'Apc']
]
现在我们的应用程序将开始使用自定义会话处理程序来读取和写入会话数据。
您可以在任何可以访问请求对象的地方访问会话数据。这意味着可以在以下地方访问会话
控制器
视图
助手
单元格
组件
在控制器、视图和单元格中使用会话的基本示例如下
$name = $this->request->getSession()->read('User.name');
// If you are accessing the session multiple times,
// you will probably want a local variable.
$session = $this->request->getSession();
$name = $session->read('User.name');
在助手中,使用 $this->getView()->getRequest()
获取请求对象;在组件中,使用 $this->getController()->getRequest()
。
您可以使用与 Hash::extract()
兼容的语法从会话中读取值
$session->read('Config.language', 'en');
与非空返回值的便捷包装器相同
$session->readOrFail('Config.language');
当您知道此键必须设置并且您不想在代码本身中检查其存在时,这很有用。
$key
应该是您希望写入 $value
的点分隔路径
$session->write('Config.language', 'en');
您也可以像这样指定一个或多个哈希
$session->write([
'Config.theme' => 'blue',
'Config.language' => 'en',
]);
当您需要从会话中删除数据时,可以使用 delete()
$session->delete('Some.value');
当您需要从会话中读取和删除数据时,可以使用 consume()
$session->consume('Some.value');
如果您想查看会话中是否存在数据,可以使用 check()
if ($session->check('Config.language')) {
// Config.language exists and is not null.
}
销毁会话在用户注销时很有用。要销毁会话,请使用 destroy()
方法
$session->destroy();
销毁会话将删除会话中的所有服务器端数据,但 **不会** 删除会话 cookie。
虽然 Authentication Plugin
在用户登录和注销时会自动更新会话 ID,但您可能需要手动旋转会话 ID。为此,请使用 renew()
方法
$session->renew();
闪存消息是只向最终用户显示一次的小消息。它们通常用于呈现错误消息或确认操作已成功执行。
要设置和显示闪存消息,您应该使用 FlashComponent 和 FlashHelper