HTTP Session
📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本
HTTP Session
簡介
由於 HTTP 驅動的應用程式是無狀態的,Session 提供了一種在多個請求之間儲存使用者資訊的方式。這些使用者資訊通常放置在持久化儲存 / 後端中,可從後續請求存取。
Laravel 附帶了多種 Session 後端,透過表達性、統一的 API 進行存取。支援 Memcached、Redis 和資料庫等熱門後端。
設定
應用程式的 Session 設定檔儲存在 config/session.php。請務必查看此檔案中可用的選項。預設情況下,Laravel 設定為使用 database Session 驅動程式。
Session driver 設定選項定義了每個請求的 Session 資料儲存位置。Laravel 包含多種驅動程式:
file- Session 儲存在storage/framework/sessions。cookie- Session 儲存在安全、加密的 cookie 中。database- Session 儲存在關聯式資料庫中。memcached/redis- Session 儲存在這些快速的快取儲存中。dynamodb- Session 儲存在 AWS DynamoDB 中。array- Session 儲存在 PHP 陣列中,不會被持久化。
[!NOTE] 陣列驅動程式主要用於測試,防止儲存在 Session 中的資料被持久化。
驅動程式先決條件
資料庫
使用 database Session 驅動程式時,您需要確保有一個資料庫表來容納 Session 資料。通常,這已包含在 Laravel 的預設 0001_01_01_000000_create_users_table.php 資料庫遷移中;但是,如果因為某種原因您沒有 sessions 表,可以使用 make:session-table Artisan 命令來生成此遷移:
php artisan make:session-table
php artisan migrate
Redis
在 Laravel 中使用 Redis Session 之前,您需要透過 PECL 安裝 PhpRedis PHP 擴充功能,或透過 Composer 安裝 predis/predis 套件(~1.0)。要了解更多關於設定 Redis 的資訊,請參閱 Laravel 的 Redis 文件。
[!NOTE]
SESSION_CONNECTION環境變數或session.php設定檔中的connection選項可用於指定用於 Session 儲存的 Redis 連線。
與 Session 互動
檢索資料
在 Laravel 中處理 Session 資料有兩種主要方式:全域 session 輔助函數和透過 Request 實例。首先,讓我們看看透過 Request 實例存取 Session,這可以在路由閉包或控制器方法中進行類型提示。請記住,控制器方法的依賴項透過 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]);
}
}
從 Session 檢索項目時,您也可以將預設值作為第二個參數傳遞給 get 方法。如果指定的索引鍵不存在於 Session 中,將返回此預設值。如果您將閉包作為預設值傳遞給 get 方法,且請求的索引鍵不存在,則會執行該閉包並返回其結果:
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
全域 Session 輔助函數
您也可以使用全域 session PHP 函數來檢索和儲存 Session 中的資料。當 session 輔助函數以單一字串參數呼叫時,它將返回該 Session 索引鍵的值。當輔助函數以索引鍵 / 值對陣列呼叫時,這些值將被儲存在 Session 中:
Route::get('/home', function () {
// 從 Session 檢索一筆資料...
$value = session('key');
// 指定預設值...
$value = session('key', 'default');
// 在 Session 中儲存一筆資料...
session(['key' => 'value']);
});
[!NOTE] 透過 HTTP 請求實例使用 Session 與使用全域
session輔助函數之間幾乎沒有實際差異。兩種方法都可以透過assertSessionHas方法進行測試。
檢索所有 Session 資料
如果您想檢索 Session 中的所有資料,可以使用 all 方法:
$data = $request->session()->all();
檢索部分 Session 資料
only 和 except 方法可用於檢索 Session 資料的子集:
$data = $request->session()->only(['username', 'email']);
$data = $request->session()->except(['username', 'email']);
判斷項目是否存在於 Session 中
要判斷某個項目是否存在於 Session 中,可以使用 has 方法。如果項目存在且不為 null,has 方法返回 true:
if ($request->session()->has('users')) {
// ...
}
要判斷某個項目是否存在於 Session 中,即使其值為 null,可以使用 exists 方法:
if ($request->session()->exists('users')) {
// ...
}
要判斷某個項目不存在於 Session 中,可以使用 missing 方法。如果項目不存在,missing 方法返回 true:
if ($request->session()->missing('users')) {
// ...
}
儲存資料
要在 Session 中儲存資料,您通常會使用請求實例的 put 方法或全域 session 輔助函數:
// 透過請求實例...
$request->session()->put('key', 'value');
// 透過全域 "session" 輔助函數...
session(['key' => 'value']);
推入陣列 Session 值
push 方法可用於將新值推入陣列類型的 Session 值。例如,如果 user.teams 索引鍵包含團隊名稱陣列,您可以這樣推入新值:
$request->session()->push('user.teams', 'developers');
檢索並刪除項目
pull 方法將在單一語句中檢索並從 Session 中刪除一個項目:
$value = $request->session()->pull('key', 'default');
遞增和遞減 Session 值
如果您的 Session 資料包含您想遞增或遞減的整數,可以使用 increment 和 decrement 方法:
$request->session()->increment('count');
$request->session()->increment('count', $incrementBy = 2);
$request->session()->decrement('count');
$request->session()->decrement('count', $decrementBy = 2);
Flash 資料
有時您可能希望將項目儲存在 Session 中供下一個請求使用。您可以使用 flash 方法來實現。使用此方法儲存在 Session 中的資料將立即可用,並在後續的 HTTP 請求中可用。在後續的 HTTP 請求之後,Flash 資料將被刪除。Flash 資料主要用於短期狀態訊息:
$request->session()->flash('status', 'Task was successful!');
如果您需要將 Flash 資料持續多個請求,可以使用 reflash 方法,它將保留所有 Flash 資料再額外一個請求。如果您只需要保留特定的 Flash 資料,可以使用 keep 方法:
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
要僅為當前請求保留 Flash 資料,可以使用 now 方法:
$request->session()->now('status', 'Task was successful!');
刪除資料
forget 方法將從 Session 中移除一筆資料。如果您想從 Session 中移除所有資料,可以使用 flush 方法:
// 忘記單一索引鍵...
$request->session()->forget('name');
// 忘記多個索引鍵...
$request->session()->forget(['name', 'status']);
$request->session()->flush();
重新生成 Session ID
重新生成 Session ID 通常是為了防止惡意使用者利用Session 固定攻擊來攻擊您的應用程式。
如果您使用 Laravel 應用程式入門套件或 Laravel Fortify,Laravel 會在認證期間自動重新生成 Session ID;但是,如果您需要手動重新生成 Session ID,可以使用 regenerate 方法:
$request->session()->regenerate();
如果您需要在單一語句中重新生成 Session ID 並從 Session 中移除所有資料,可以使用 invalidate 方法:
$request->session()->invalidate();
Session 快取
Laravel 的 Session 快取提供了一種方便的方式來快取限定於個別使用者 Session 的資料。與全域應用程式快取不同,Session 快取資料會自動按 Session 隔離,並在 Session 過期或被銷毀時清理。Session 快取支援所有常見的 Laravel 快取方法,如 get、put、remember、forget 等,但限定於當前 Session。
Session 快取非常適合儲存您希望在同一 Session 內跨多個請求持久化但不需要永久儲存的臨時、使用者特定資料。這包括表單資料、臨時計算、API 響應或任何其他應綁定到特定使用者 Session 的暫時資料。
您可以透過 Session 上的 cache 方法存取 Session 快取:
$discount = $request->session()->cache()->get('discount');
$request->session()->cache()->put(
'discount', 10, now()->plus(minutes: 5)
);
要了解更多關於 Laravel 快取方法的資訊,請參閱快取文件。
Session 阻塞
[!WARNING] 要使用 Session 阻塞,您的應用程式必須使用支援原子鎖的快取驅動程式。目前,這些快取驅動程式包括
memcached、dynamodb、redis、mongodb(包含在官方mongodb/laravel-mongodb套件中)、database、file和array驅動程式。此外,您不能使用cookieSession 驅動程式。
預設情況下,Laravel 允許使用相同 Session 的請求並發執行。因此,例如,如果您使用 JavaScript HTTP 函式庫向應用程式發出兩個 HTTP 請求,它們將同時執行。對於大多數應用程式來說,這不是問題;但是,對於一小部分向兩個不同的應用程式端點並發發出請求且兩者都向 Session 寫入資料的應用程式,可能會發生 Session 資料遺失。
為了緩解此問題,Laravel 提供了允許您限制給定 Session 並發請求的功能。首先,您可以簡單地在路由定義上鏈接 block 方法。在此範例中,傳入 /profile 端點的請求將獲取一個 Session 鎖。當此鎖被持有时,任何傳入 /profile 或 /order 端點且共享相同 Session ID 的請求將等待第一個請求完成執行後才繼續其執行:
Route::post('/profile', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);
Route::post('/order', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);
block 方法接受兩個可選參數。第一個參數是 Session 鎖在釋放前應持有的最大秒數。當然,如果請求在此時間之前完成執行,鎖將提前釋放。
第二個參數是請求在嘗試獲取 Session 鎖時應等待的秒數。如果請求無法在給定秒數內獲取 Session 鎖,將拋出 Illuminate\Contracts\Cache\LockTimeoutException。
如果兩個參數都未傳遞,鎖將最多持有 10 秒,請求在嘗試獲取鎖時最多等待 10 秒:
Route::post('/profile', function () {
// ...
})->block();
新增自訂 Session 驅動程式
實作驅動程式
如果現有的 Session 驅動程式都不符合您應用程式的需求,Laravel 讓您有可能編寫自己的 Session 處理器。您的自訂 Session 驅動程式應實作 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。
由於這些方法的目的不容易理解,以下是每個方法的用途概述:
open方法通常用於基於檔案的 Session 儲存系統。由於 Laravel 附帶fileSession 驅動程式,您很少需要在此方法中放入任何內容。您可以簡單地將此方法留空。close方法與open方法一樣,通常也可以被忽略。對於大多數驅動程式,它不需要。write方法應將給定的$data字串與$sessionId一起寫入某個持久化儲存系統。同樣地,您不應執行任何序列化 — Laravel 已經為您處理了。destroy方法應從持久化儲存中移除與$sessionId相關的資料。gc方法應銷毀所有比給定$lifetime更舊的 Session 資料。對於像 Memcached 和 Redis 這樣的自動過期系統,此方法可以留空。
註冊驅動程式
驅動程式實作完成後,您就可以將其註冊到 Laravel。要向 Laravel 的 Session 後端新增額外的驅動程式,可以使用 Session facade 提供的 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;
});
}
}
Session 驅動程式註冊後,您可以使用 SESSION_DRIVER 環境變數或在應用程式的 config/session.php 設定檔中將 mongo 驅動程式指定為應用程式的 Session 驅動程式。