鉴于 HTTP 驱动的应用程序是无状态的,会话提供了一种在多个请求之间存储用户信息的方式。该用户信息通常存储在持久化存储/后端中,可供后续请求访问。
Laravel 附带了多种会话后端,可通过富有表现力的统一 API 进行访问。对 Memcached、Redis 和数据库等流行后端的支持已包含在内。
您应用的会话配置文件存储在 config/session.php。请务必查阅此文件中可用的选项。默认情况下,Laravel 配置为使用 database 会话驱动。
会话 driver 配置选项定义了每个请求的会话数据存储位置。Laravel 包含了多种驱动:
[!NOTE]
数组驱动主要用于测试,并阻止会话中存储的数据持久化。
当使用 database 会话驱动程序时,您需要确保有一个数据库表来存储会话数据。通常,这包含在 Laravel 的默认 0001_01_01_000000_create_users_table.php 数据库迁移中;但是,如果由于任何原因您没有 sessions 表,您可以使用 make:session-table Artisan 命令来生成此迁移:
php artisan make:session-table
php artisan migrate在使用 Laravel 的 Redis 会话之前,你需要通过 PECL 安装 PhpRedis PHP 扩展,或者通过 Composer 安装 predis/predis 包 (~1.0)。有关配置 Redis 的更多信息,请查阅 Laravel 的 Redis 文档。
[!NOTE]
SESSION_CONNECTION环境变量,或session.php配置文件中的connection选项,可用于指定使用哪个 Redis 连接进行会话存储。
在 Laravel 中,处理会话数据主要有两种方式:全局 session 辅助函数和通过 Request 实例。首先,我们来看看如何通过 Request 实例访问会话,该实例可以在路由闭包或控制器方法中进行类型提示。请记住,控制器方法的依赖项会通过 Laravel 的 服务容器 自动注入:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*/
public function show(Request $request, string $id): View
{
$value = $request->session()->get('key');
// ...
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}当你从会话中获取一个项时,你也可以将一个默认值作为第二个参数传递给 get 方法。这个默认值将会在指定的键不存在于会话中时被返回。如果你将一个闭包作为默认值传递给 get 方法 并且请求的键不存在,该闭包将被执行 并且其结果被返回:
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});您也可以使用全局 session PHP 函数来获取和存储数据到会话中。当调用 session 辅助函数并传入单个字符串参数时,它将返回该会话键的值。当调用该辅助函数并传入一个键/值对数组时,这些值将被存储到会话中:
Route::get('/home', function () {
// Retrieve a piece of data from the session...
$value = session('key');
// Specifying a default value...
$value = session('key', 'default');
// Store a piece of data in the session...
session(['key' => 'value']);
});[!注意]
通过 HTTP 请求实例使用会话与使用全局session辅助函数之间,实际上几乎没有区别。两种方法都可测试,通过在你的所有测试用例中都可用的assertSessionHas方法。
如果您想检索会话中的所有数据,您可以使用 all 方法:
$data = $request->session()->all();该 only 和 except 方法可用于检索会话数据的子集:
$data = $request->session()->only(['username', 'email']);
$data = $request->session()->except(['username', 'email']);要确定会话中是否存在某个项,您可以使用 has 方法。 has 方法会在该项存在且不为 null 时返回 true:
if ($request->session()->has('users')) {
// ...
}为了确定会话中是否存在某个项目,即使其值为 null,您可以使用 exists 方法:
if ($request->session()->exists('users')) {
// ...
}为了确定某个项是否不在会话中,您可以使用 missing 方法。如果该项不存在,missing 方法将返回 true:
if ($request->session()->missing('users')) {
// ...
}要在会话中存储数据,你通常会使用请求实例的 put 方法或全局 session 辅助函数:
// Via a request instance...
$request->session()->put('key', 'value');
// Via the global "session" helper...
session(['key' => 'value']);push 方法可用于将新值推送到作为数组的会话值中。例如,如果 user.teams 键包含一个团队名称数组,您可以像这样将一个新值推送到该数组中:
$request->session()->push('user.teams', 'developers');该 pull 方法将在一个语句中从会话中检索并删除一个项:
$value = $request->session()->pull('key', 'default');如果您的会话数据包含您希望递增或递减的整数,您可以使用 increment 和 decrement 方法:
$request->session()->increment('count');
$request->session()->increment('count', $incrementBy = 2);
$request->session()->decrement('count');
$request->session()->decrement('count', $decrementBy = 2);有时您可能希望在会话中为下一个请求存储项目。您可以使用 flash 方法来完成此操作。使用此方法存储在会话中的数据将立即可用,并在随后的 HTTP 请求期间可用。在随后的 HTTP 请求之后,闪存数据将被删除。闪存数据主要用于短期状态消息:
$request->session()->flash('status', 'Task was successful!');如果您需要跨越多个请求持久化您的闪存数据, 您可以使用 reflash 方法, 它将把所有闪存数据再保留一个请求. 如果您只需要保留特定的闪存数据, 您可以使用 keep 方法:
$request->session()->reflash();
$request->session()->keep(['username', 'email']);若要使您的闪存数据仅在当前请求中持久化,您可以使用 now 方法:
$request->session()->now('status', 'Task was successful!');该 forget 方法 将 移除 一 条 的 数据 从 该 会话。 如果 您 将 希望 去 移除 所有 数据 从 该 会话, 您 可以 使用 该 flush 方法:
// Forget a single key...
$request->session()->forget('name');
// Forget multiple keys...
$request->session()->forget(['name', 'status']);
$request->session()->flush();重新生成会话 ID 通常是为了防止恶意用户利用 会话固定 攻击您的应用程序。
Laravel 在认证期间会自动重新生成会话 ID,如果你正在使用 Laravel 的某个应用快速入门套件或Laravel Fortify;但是,如果你需要手动重新生成会话 ID,你可以使用 regenerate 方法:
$request->session()->regenerate();如果你需要在单个语句中重新生成会话 ID 并从会话中移除所有数据,你可以使用 invalidate 方法:
$request->session()->invalidate();Laravel 的会话缓存提供了一种便捷的方式,用于缓存限定于单个用户会话的数据。与全局应用缓存不同,会话缓存数据会自动按会话隔离,并在会话过期或销毁时进行清理。会话缓存支持所有熟悉的 Laravel 缓存方法,例如 get、put、remember、forget 等,但限定于当前会话。
会话缓存非常适合存储临时性的, 用户特定的数据, 您希望其在同一会话的多个请求中保持持久化, 但不需要永久存储。 这包括表单数据, 临时计算结果, API 响应, 或任何其他临时数据, 应绑定到特定用户会话。
你可以通过会话上的 cache 方法访问会话缓存:
$discount = $request->session()->cache()->get('discount');
$request->session()->cache()->put(
'discount', 10, now()->addMinutes(5)
);有关 Laravel 缓存方法的更多信息,请查阅缓存文档。
[!警告]
要使用会话阻塞功能,您的应用程序必须使用支持原子锁的缓存驱动。目前,这些缓存驱动包括memcached、dynamodb、redis、mongodb(包含在官方的mongodb/laravel-mongodb包中)、database、file和array驱动。此外,您不能使用cookie会话驱动。
默认情况下,Laravel 允许使用相同会话的请求并发执行。 所以,例如,如果您使用 JavaScript HTTP 库向您的应用程序发出两个 HTTP 请求,它们将同时执行。 对于许多应用程序来说,这并不是问题; 然而,在少数向两个不同应用程序端点发出并发请求且这两个端点都向会话写入数据的应用程序中,可能会发生会话数据丢失。
为了缓解此问题,Laravel 提供了一项功能,允许你限制给定会话的并发请求。要开始使用,你可以简单地将 block 方法链式调用到你的路由定义上。在此示例中,一个到 /profile 端点的传入请求将获取一个会话锁。在此锁被持有期间,任何共享相同会话 ID 并传入到 /profile 或 /order 端点的请求,将等待第一个请求完成执行,然后才继续它们的执行:
Route::post('/profile', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);
Route::post('/order', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);该 block 方法接受两个可选参数。该 block 方法接受的第一个参数是会话锁在被释放前应保持的最大秒数。当然,如果请求在此时间之前完成执行,锁将更早被释放。
block 方法接受的第二个参数是请求在尝试获取会话锁时应等待的秒数。如果请求在给定秒数内无法获取会话锁,则会抛出 Illuminate\Contracts\Cache\LockTimeoutException。
如果这两个参数都没有传递,锁将最长保持10秒,并且请求在尝试获取锁时将最长等待10秒:
Route::post('/profile', function () {
// ...
})->block();如果现有会话驱动程序均不符合你的应用程序需求,Laravel 允许你编写自己的会话处理器。你的自定义会话驱动程序应实现 PHP 内置的 SessionHandlerInterface。此接口仅包含几个简单方法。一个存根 MongoDB 实现示例如下:
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}由于 Laravel 不包含用于存放你的扩展的默认目录。你可以随意将它们放置在你喜欢的任何位置。在这个例子中,我们创建了一个 Extensions 目录来存放 MongoSessionHandler。
鉴于这些方法的目的不易理解,以下概述了每种方法的目的:
您的驱动程序实现后,即可在 Laravel 中注册它。要为 Laravel 的会话后端添加额外的驱动程序,您可以使用 Session 门面 提供的 extend 方法。您应该从 服务提供者 的 boot 方法中调用 extend 方法。您可以从现有的 App\Providers\AppServiceProvider 执行此操作,或创建一个全新的提供者:
<?php
namespace App\Providers;
use App\Extensions\MongoSessionHandler;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
class SessionServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Session::extend('mongo', function (Application $app) {
// Return an implementation of SessionHandlerInterface...
return new MongoSessionHandler;
});
}
}会话驱动注册完成后,你可以将 mongo 驱动指定为你的应用的会话驱动,使用 SESSION_DRIVER 环境变量或在应用的 config/session.php 配置文件中。