HTTPセッション
はじめに
HTTP駆動アプリケーションはステートレスであるため、セッションは複数のリクエストにわたってユーザーに関する情報を保存する方法を提供します。そのユーザー情報は通常、後続のリクエストからアクセスできる永続的なストア/バックエンドに配置されます。
Laravelには、表現力豊かで統一されたAPIを通じてアクセスできるさまざまなセッションバックエンドが付属しています。 Memcached、Redis、データベースなどの一般的なバックエンドのサポートが含まれています。
設定
アプリケーションのセッション構成ファイルはconfig/session.php
に保存されています。このファイルで利用可能なオプションを必ず確認してください。デフォルトでは、Laravelはdatabase
セッションドライバを使用するように構成されています。
セッションのdriver
構成オプションは、各リクエストのセッションデータが保存される場所を定義します。Laravelにはさまざまなドライバが含まれています
-
file
- セッションはstorage/framework/sessions
に保存されます。 -
cookie
- セッションは、安全な暗号化されたCookieに保存されます。 -
database
- セッションは、リレーショナルデータベースに保存されます。 -
memcached
/redis
- セッションは、これらの高速なキャッシュベースのストアのいずれかに保存されます。 -
dynamodb
- セッションは、AWS DynamoDBに保存されます。 -
array
- セッションはPHP配列に保存され、永続化されません。
配列ドライバは、主にテスト中に使用され、セッションに保存されたデータが永続化されるのを防ぎます。
ドライバの前提条件
データベース
database
セッションドライバを使用する場合は、セッションデータを格納するデータベーステーブルがあることを確認する必要があります。通常、これは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セッションを使用する前に、PECL経由でPhpRedis PHP拡張機能をインストールするか、Composer経由でpredis/predis
パッケージ(〜1.0)をインストールする必要があります。Redisの構成の詳細については、LaravelのRedisドキュメントを参照してください。
SESSION_CONNECTION
環境変数、またはsession.php
構成ファイルのconnection
オプションを使用して、セッションストレージに使用するRedis接続を指定できます。
セッションの操作
データの取得
Laravelでセッションデータを操作するには、グローバルsession
ヘルパーとRequest
インスタンスを介した2つの主要な方法があります。最初に、ルートクロージャまたはコントローラメソッドでタイプヒントできる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
メソッドへの2番目の引数としてデフォルト値を渡すこともできます。指定されたキーがセッションに存在しない場合、このデフォルト値が返されます。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の再生成
セッションIDの再生成は、悪意のあるユーザーがアプリケーションに対するセッション固定攻撃を悪用するのを防ぐためによく行われます。
Laravelでは、LaravelのアプリケーションスターターキットまたはLaravel Fortifyのいずれかを使用している場合、認証中にセッションIDが自動的に再生成されます。ただし、セッションIDを手動で再生成する必要がある場合は、regenerate
メソッドを使用できます。
$request->session()->regenerate();
セッションIDを再生成し、セッションからすべてのデータを1つのステートメントで削除する必要がある場合は、invalidate
メソッドを使用できます。
$request->session()->invalidate();
セッションブロッキング
セッションブロッキングを利用するには、アプリケーションがアトミックロックをサポートするキャッシュドライバーを使用している必要があります。現在、これらのキャッシュドライバーには、memcached
、dynamodb
、redis
、mongodb
(公式のmongodb/laravel-mongodb
パッケージに含まれています)、database
、file
、およびarray
ドライバーが含まれています。さらに、cookie
セッションドライバーを使用することはできません。
デフォルトでは、Laravelは同じセッションを使用するリクエストを同時に実行することを許可しています。たとえば、JavaScriptのHTTPライブラリを使用してアプリケーションに2つのHTTPリクエストを送信した場合、両方が同時に実行されます。多くのアプリケーションではこれは問題ではありませんが、セッションにデータを書き込む2つの異なるアプリケーションエンドポイントに同時リクエストを行う一部のアプリケーションでは、セッションデータの損失が発生する可能性があります。
これを軽減するために、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
メソッドは、2つのオプションの引数を受け入れます。block
メソッドで受け入れられる最初の引数は、セッションロックが解放されるまでに保持される最大秒数です。もちろん、要求がこの時間よりも早く実行を完了した場合、ロックはそれよりも早く解放されます。
block
メソッドで受け入れられる2番目の引数は、セッションロックの取得を試行中にリクエストが待機する秒数です。指定された秒数以内にリクエストがセッションロックを取得できない場合、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には拡張機能を格納するためのデフォルトのディレクトリが含まれていないため、どこにでも自由に配置できます。この例では、MongoSessionHandler
を格納するためにExtensions
ディレクトリを作成しました。
これらのメソッドの目的は容易に理解できないため、各メソッドの目的の概要を次に示します。
open
メソッドは、通常、ファイルベースのセッションストアシステムで使用されます。Laravelにはfile
セッションドライバーが付属しているため、このメソッドに何かを入れる必要はほとんどありません。このメソッドは空のままにしておくことができます。close
メソッドは、open
メソッドと同様に、通常は無視できます。ほとんどのドライバーでは必要ありません。read
メソッドは、指定された$sessionId
に関連付けられたセッションデータの文字列バージョンを返す必要があります。Laravelがシリアル化を実行するため、ドライバーでセッションデータを取得または保存するときに、シリアル化やその他のエンコードを行う必要はありません。write
メソッドは、指定された$data
文字列を$sessionId
に関連付けて、MongoDBなどの永続ストレージシステムまたは選択した別のストレージシステムに書き込む必要があります。繰り返しますが、シリアル化を実行しないでください。Laravelが既に処理しています。destroy
メソッドは、$sessionId
に関連付けられたデータを永続ストレージから削除する必要があります。gc
メソッドは、指定された$lifetime
(UNIXタイムスタンプ)よりも古いすべてのセッションデータを破棄する必要があります。MemcachedやRedisのような自己期限切れシステムの場合、このメソッドは空のままにできます。
ドライバの登録
ドライバーが実装されたら、Laravelに登録する準備ができました。Laravelのセッションバックエンドにドライバーを追加するには、Session
ファサードによって提供されるextend
メソッドを使用できます。extend
メソッドは、サービスプロバイダーのboot
メソッドから呼び出す必要があります。既存の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_DRIVER
環境変数を使用するか、アプリケーションのconfig/session.php
構成ファイル内で、アプリケーションのセッションドライバーとしてmongo
ドライバーを指定できます。