投稿理由
マルチログインでセッションをDB管理する場合、最低でもテーブルにカラムを管理者側とユーザーで用意する必要があります。
ですが、実装例として参考になる記事が少なく苦労したので、後発者の参考になればと思い投稿しました。
DatabaseSessionHandler.phpを普通にオーバーライドしても読み込んでくれなかったのでここがポイントになります。
Laravel経験が浅いため、誤っている箇所があるかもしれません。
環境
Laravel9系
MYSQL
Docker
VSCode(Dev Containers使用)
前提
マルチログイン実装済みのものとします。
Laravel マルチログインで調べればいくらでも出てくるのでここでは割愛します。
全体像
最終的な修正ファイルのディレクトリ
修正対象以外はすべて省略しています。
src/
├ app/
│ ├ Providers/
│ │ └ SessionServiceProvider.php 新規作成
│ └ Session/
│ └ DatabaseSessionHandler.php 新規作成
│
├ config/
│ ├ app.php 既存を修正
│ └ session.php 既存を修正
│
└ database/migrations/
└ yyyy_mm_dd_△_create_sessions_table.php 新規作成
修正内容
①config/session.php
'driver' => env('SESSION_DRIVER', 'file'),
// ↓下へ修正
'driver' => 'database',
②Session管理用テーブルの作成
以下のコマンドを実行
php artisan session:table
③ ②で作成したマイグレーションファイルを修正
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->foreignId('admin_user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('sessions');
}
};
④テーブルの作成
php artisan migrate
⑤app/Providers/SessionServiceProvider.php
作成して以下の内容にする。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
use App\Session\DatabaseSessionHandler;
class SessionServiceProvider extends ServiceProvider
{
public function boot()
{
Session::extend('database', function ($app) {
$connection_name = $this->app->config->get('session.connection');
$connection = $app->app->db->connection($connection_name);
$table = $this->app->config->get('session.table');
$lifetime = $this->app->config->get('session.lifetime');
return new DatabaseSessionHandler($connection, $table, $lifetime, $this->app);
});
}
}
⑥app/Session/DatabaseSessionHandler.php
作成して以下の内容にする。
一部書き換える必要がありますが、マルチログインの場合URLが系統ごとに異なってる場合が多いと思うのでそこからとればいいと思います。
Authから取ろうとしましたが、うまくとれずこの形になってます。もっとよい形があると思います。
<?php
namespace App\Session;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Container\Container;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\Route;
class DatabaseSessionHandler extends \Illuminate\Session\DatabaseSessionHandler
{
/**
* Add the user information to the session payload.
*
* @param array $payload
* @return $this
*/
protected function addUserInformation(&$payload)
{
if ($this->container->bound(Guard::class)) {
// Authから取得できなかったのでURLから画面系統を取得
$guard = isset(explode("\\", Route::currentRouteAction())[キー]) ? explode("\\", Route::currentRouteAction())[キー] : '';
# ここに条件を入れる。
if ($guard === '画面系統') {
$payload['user_id'] = $this->userId();
# ここに条件を入れる。
} elseif ($guard === '画面系統') {
$payload['admin_user_id'] = $this->userId();
}
}
return $this;
}
}
以上でDBで管理できます。
補足 テーブルを分けず、カラムを分けるだけの実装理由について
テーブルを分けると認証が切れるためです。
※管理者側ログイン→ユーザーログイン とすると管理者側がセッション切れしてしまいました。逆でも同様の現象になります。
別テーブルでも良いようにさらにオーバーライドして修正してもよいですが、そこまで困らないので()
終わりに
後発者の方の参考になれば幸いです。