HTTP Session
简介
由于 HTTP 应用是无状态的,因此 Session 提供了一种在多个请求间存储用户相关信息的方法。Laravel 通过一个可读性强、统一的 API 访问处理各种自带的 Session 后端驱动。支持热门的诸如 Memcached,Redis 和开箱即用的数据库等驱动。
配置
Session 配置存储在 config/session.php
文件中。确保查看该文件中可用的选项。默认情况下,Laravel 配置使用 file
Session 驱动,该驱动适用于许多应用。对于生产环境中的应用,可以考虑使用 memcached
或 redis
驱动来获取更快的 Session 性能。
Session 的 driver
配置项定义了每个请求存储 Session 数据的位置。Laravel 配备几个开箱即用的驱动:
file
- Session 存储在storage/framework/sessions
。cookie
- Session 存储在安全加密的 Cookie 中。database
- Session 存储在一个关系型数据库中。memcached
/redis
- Session 存储在其中一个基于缓存的快速存储系统中。array
- Session 存储在一个 PHP 数组中,不会被持久化。
数组驱动在 测试 时使用,防止 Session 中存储的数据被持久化。
驱动前提
数据库
使用 database
Session 驱动时,需要创建一张包含 Session 数据的表。以下是 Schema
声明该表的示例:
Schema::create('sessions', function ($table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
});
可以使用 Artisan 命令 session:table
来生成该迁移:
php artisan session:table
php artisan migrate
Redis
使用 Laravel 的 Redis 驱动之前,需要通过 Composer 安装 predis/predis
扩展包(~1.0
)。可以在 database
配置文件中配置 Redis 连接。在 session
配置文件中,connection
选项可用于指定 Session 使用哪个 Redis 连接。
使用 Session
获取数据
在 Laravel 中主要有两种方法来处理 Session 数据:全局辅助函数 session
以及通过 Request
实例。首先,让我们看一下使用 Request
实例获取 Session,可以在控制器方法中对该实例使用类型提示。请记住,控制器方法的依赖会通过 Laravel 服务容器 自动注入:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* 显示给定用户的配置信息
*
* @param Request $request
* @param int $id
* @return Response
*/
public function show(Request $request, $id)
{
$value = $request->session()->get('key');
//
}
}
当从 Session 中获取一项数据时,也可以将默认值作为第二个参数传递给 get
方法。如果指定的键在 Session 中不存在,则会返回该默认值。如果将闭包作为默认值传递给 get
方法,并且所请求的键不存在时,则会执行该闭包并返回其结果:
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
全局 Session 辅助函数
也可以使用全局辅助函数 session
来获取存储在 Session 中的数据。当用一个字符串参数调用 session
辅助函数时,会返回该 Session 键的值。当用键/值对调用时,这些值将会被存储到 Session 中:
Route::get('home', function () {
// 获取 Session 中 key 的值
$value = session('key');
// 指定一个默认值.
$value = session('key', 'default');
// 向 Session 中存储数据
session(['key' => 'value']);
});
通过 HTTP 请求实例使用 Session 和使用
session
辅助函数几乎没有实际区别。在所有测试用例中,两种方法都可以通过assertSessionHas
方法进行 测试。
获取所有 Session 数据
如果要获取 Session 中的所有数据,可以使用 all
方法:
$data = $request->session()->all();
判断 Session 中是否存在一项数据
要判断 Session 中是否存在一项数据,可以使用 has
方法。如果指定项存在并且不为 null
,has
方法会返回 true
:
if ($request->session()->has('users')) {
//
}
如果要判断 Session 中是否存在某项数据,并允许其值为 null
,可以使用 exists
方法。如果该项数据存在,exists
方法会返回 true
:
if ($request->session()->exists('users')) {
//
}
存储数据
要在 Session 中存储数据,通常使用 put
方法或者 session
辅助函数:
// 通过请求实例
$request->session()->put('key', 'value');
// 通过全局辅助函数
session(['key' => 'value']);
在 Session 数组中存储数据
push
方法用于当 Session 的值是一个数组时,将新的值添加到里面。例如,如果 user.teams
键对应一个值为团队名称的数组,可以像这样将新值添加到数组中:
$request->session()->push('user.teams', 'developers');
获取 & 删除一项数据
pull
方法会在一条语句中,从 Session 获取并删除一项数据:
$value = $request->session()->pull('key', 'default');
闪存数据
有时您可能希望在 Session 中仅为下次请求存储某些数据。可以使用 flash
方法进行此操作。使用该方法在 Session 中存储的数据只会在随后的 HTTP 请求中可用,然后将被删除。闪存数据主要用于短期的状态信息:
$request->session()->flash('status', 'Task was successful!');
如果要在几个请求之前保留闪存数据,可以使用 reflash
方法,该方法将为其它请求保留所有闪存数据。如果只需要保留指定的闪存数据,可以使用 keep
方法:
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
删除数据
forget
方法会从 Session 中移除部分数据。如果要从 Session 中移除所有数据,可以使用 flush
方法:
$request->session()->forget('key');
$request->session()->flush();
重新生成 Session ID
重新生成 Session ID,通常是为了防止恶意用户利用 Session fixation 对应用进行攻击。
如果使用内置的 LoginController
,Laravel 会在身份认证时自动重新生成 Session ID;如果需要手动重新生成 Session ID,可以使用 regenerate
方法:
$request->session()->regenerate();
添加自定义 Session 驱动
实现驱动
自定义 Session 驱动应该实现 SessionHandlerInterface
接口。该接口只包含一些我们要实现的简单方法。一个缺省的 MongoDB 实现应该看起来像这样:
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
。
由于这些方法不容易理解,让我们快速介绍每个方法的作用:
open
方法通常用在基于文件的 Session 存储系统。由于 Laravel 自带了一个file
Session 驱动,因此您几乎不需要在该方法中放任何东西。可以将该方法留空。这是一个糟糕的接口设计(我们会稍后讨论),PHP 要求我们实现该方法。close
方法,和open
方法一样,通常也可以无视。对于大多数驱动来说,都不需要。read
方法应该返回与给定$sessionId
关联的 Session 数据的字符串形式。当从驱动中获取 Session 数据时,您无需进行任何序列化或其它编码,因为 Laravel 会自动为您进行序列化。write
方法应该将与$sessionId
关联的给定$data
字符串写入到一些持久化存储系统,例如 MongoDB,Dynamo 等。同样,您不应进行任何序列化 —— Laravel 会为您处理。destroy
方法应该将与$sessionId
关联的 Session 数据从持久化存储中移除。gc
方法应该销毁所有给定$lifetime
之前的所有 Session 数据,它是一个 UNIX 时间戳。对本身拥有过期机制的系统如 Memcached 和 Redis,该方法可以留空。
注册驱动
驱动实现之后,就可以在框架中注册它了。要为 Laravel Session 后端添加其它驱动,可以使用 Session
Facade 的 extend
方法。应该在 服务容器 的 boot
方法中调用 extend
方法。可以在现有的 AppServiceProvider
或创建一个全新的服务提供者:
namespace App\Providers;
use App\Extensions\MongoSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
class SessionServiceProvider extends ServiceProvider
{
/**
* 启动已注册服务
*
* @return void
*/
public function boot()
{
Session::extend('mongo', function ($app) {
// 返回 SessionHandlerInterface 接口的实现
return new MongoSessionHandler;
});
}
/**
* 注册容器绑定
*
* @return void
*/
public function register()
{
//
}
}
注册 Session 驱动后,就可以在 config/session.php
配置文件中使用 mongo
驱动了。