【Laravel】マルチログイン機能を作ってみる(Laravel11, 10)
はじめに
Laravelを使ったマルチログイン機能を作りましょう。
管理者用のログイン画面を作り、ユーザーとは全く別のログイン機能を作ります。
更新履歴
- 2024/03/13 Laravel11対応
- 2023/06/12 公開
環境
- Laravel10
- PHP 8.2.3
- Laravel Framework 10.13.1
 
- Laravel11
- PHP 8.2.16
- Laravel Framework 11.0.3
 
設計
画面レイアウト設計
管理ログイン画面

管理トップ画面

テーブル定義
Migrationクラスで追加するadminsテーブル定義です。
テーブルレコード
Seederクラスで追加するadminsテーブルのレコードです。
| id | name | password | created_at | updated_at | 
|---|---|---|---|---|
| 1 | admin_01 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 2 | admin_02 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 3 | admin_03 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 4 | admin_04 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 5 | admin_05 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 6 | admin_06 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 7 | admin_07 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 8 | admin_08 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 9 | admin_09 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
| 10 | admin_10 | admin | 2022-12-30 11:22:33 | 2022-12-31 23:58:59 | 
データベース
各ファイル作成
Adminモデル及び、関連クラスを作成しましょう。
php artisan make:model Admin -mfs
# 以下が追加される
#   app/Models/Admin.php
#   database/factories/AdminFactory.php
#   database/migrations/YYYY_MM_DD_hhmmss_create_admins_table.php
#   database/seeders/AdminSeeder.php
Modelクラス
追加されたAdminモデルの親クラスを\Illuminate\Foundation\Auth\Userクラスに変更しましょう。
app/Models/Admin.php
 <?php
 
 namespace App\Models;
 
 use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Model;
+use Illuminate\Foundation\Auth\User as Authenticatable;
 
-class Admin extends Model
+class Admin extends Authenticatable
 {
     use HasFactory;
 }
テーブル作成準備
Migrationクラス
adminsテーブル定義をMigrationクラスのupメソッドに記述しましょう。
追加済みのMigrationクラス(YYYY_MM_DD_HHmmss_create_admins_table.php)のupメソッドに
nameカラムとpasswordカラムを追加します。
database/migrations/YYYY_MM_DD_HHmmss_create_admins_table.php
 <?php
 
 use Illuminate\Database\Migrations\Migration;
 use Illuminate\Database\Schema\Blueprint;
 use Illuminate\Support\Facades\Schema;
 
 return new class extends Migration
 {
     /**
      * Run the migrations.
      */
     public function up(): void
     {
         Schema::create('admins', function (Blueprint $table) {
             $table->id();
+            $table->string('name')->unique()->comment('名称');
+            $table->string('password')->comment('パスワード');
             $table->timestamps();
         });
     }
 
     /**
      * Reverse the migrations.
      */
     public function down(): void
     {
         Schema::dropIfExists('admins');
     }
 };
Seederクラス
adminsテーブルにレコード10件(nameがadmin_01〜admin_10、passwordがadmin)を追加するよう
Seeder::run メソッドに記述しましょう。
database/seeders/AdminSeeder.php
 <?php
 
 namespace Database\Seeders;
 
+use App\Models\Admin;
 use Illuminate\Database\Console\Seeds\WithoutModelEvents;
 use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\Hash;
 
 class AdminSeeder extends Seeder
 {
     /**
      * Run the database seeds.
      */
     public function run(): void
     {
         //
+        if (app()->isLocal()) {
+            // 開発環境のみレコードを追加
+            Admin::factory()
+                ->count(10)
+                ->sequence(function ($sequence) {
+                    return [
+                        'name' => sprintf('admin_%02d', $sequence->index + 1),
+                        'password' => Hash::make('admin'), // パスワード: admin  ※ 開発環境用のパスワードのためソース埋め込み
+                        'created_at' => '2022-12-30 11:22:33',
+                        'updated_at' => '2022-12-31 23:58:59',
+                    ];
+                })
+                ->create();
+        }
     }
 }
database/seeders/DatabaseSeeder.php
 <?php
 
 namespace Database\Seeders;
 
 use App\Models\User;
 // use Illuminate\Database\Console\Seeds\WithoutModelEvents;
 use Illuminate\Database\Seeder;
 
 class DatabaseSeeder extends Seeder
 {
     /**
      * Seed the application's database.
      */
     public function run(): void
     {
+        $this->call([
+            AdminSeeder::class,
+        ]);
         // User::factory(10)->create();
 
         User::factory()->create([
             'name' => 'Test User',
             'email' => 'test@example.com',
         ]);
     }
 }
テーブル作成
MigrationとSeederを実行をしましょう。
これにより、自動的にテーブル・レコードが作成されます。
# Running Migrations
php artisan migrate
# Running Seeders
php artisan db:seed
誤ってテーブルが作成してしまった場合
以下のコマンドを実行すると、全てのテーブルを削除し
再度、MigrationとSeederを実行します。
# 全テーブル初期化(全テーブル削除 → Migrations実行 → Seeders実行)
php artisan migrate:fresh --seed
ガードとプロバイダ設定
config/auth.php ファイルのプロバイダにadmins、ガードにadminを追加しましょう。
これによりauthミドルウェアのadminガード指定が可能になります。
config/auth.php
コメント部分は省略
 <?php
 return [
     'defaults' => [
         'guard' => env('AUTH_GUARD', 'web'),
         'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
     ],
     'guards' => [
         'web' => [
             'driver' => 'session',
             'provider' => 'users',
         ],
+        'admin' => [
+            'driver' => 'session',
+            'provider' => 'admins',
+        ],
     ],
     'providers' => [
         'users' => [
             'driver' => 'eloquent',
             'model' => env('AUTH_MODEL', App\Models\User::class),
         ],
         // 'users' => [
         //     'driver' => 'database',
         //     'table' => 'users',
         // ],
+        'admins' => [
+            'driver' => 'eloquent',
+            'model' => App\Models\Admin::class,
+        ],
     ],
     'passwords' => [
         'users' => [
             'provider' => 'users',
             'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
             'expire' => 60,
             'throttle' => 60,
         ],
     ],
     'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
 ];
Requestクラス
ログイン情報のバリデーションを行う、フォームリクエストを作成します。
php artisan make:request AdminLoginRequest
app/Http/Requests/AdminLoginRequest.php
以下のauthenticateメソッドはControllerから
手動で呼び出すメソッドとして追加しています。
 <?php
 
 namespace App\Http\Requests;
 
 use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Validation\ValidationException;
 
 class AdminLoginRequest extends FormRequest
 {
     /**
      * Determine if the user is authorized to make this request.
      */
     public function authorize(): bool
     {
-        return false;
+        return true;
     }
 
     /**
      * Get the validation rules that apply to the request.
      *
      * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
      */
     public function rules(): array
     {
         return [
-            //
+            'name' => 'required',
+            'password' => 'required',
         ];
     }
+
+    public function authenticate(): void
+    {
+        if (!Auth::guard('admin')->attempt($this->only('name', 'password'))) {
+            throw ValidationException::withMessages(['failed' => __('auth.failed')]);
+        }
+    }
 }
__('auth.failed') は何をしている?
__は言語ファイルから指定キーのメッセージを取得するヘルパ関数です。
lang/xx/auth.phpファイルのfailedキーのメッセージを取得しています。
参考
スターターキット Laravel Breeze の\App\Http\Requests\Auth\LoginRequestクラスを参考にしています。
Routing・Middleware・View・Controller
ルート一覧
これから追加すルートの一覧です。
管理トップ画面は、管理ログイン後のみアクセス可能にします。
| HTTPメソッド | URI | ルート名 | Controller@method | 機能 | 
|---|---|---|---|---|
| GET | HEAD | admin | admin.top | - | 管理トップ画面🔑 | 
| GET | HEAD | admin-login | admin.login | AdminLoginController@create | 管理ログイン画面 | 
| POST | admin-login | admin.login.store | AdminLoginController@store | 管理ログイン | 
| DELETE | admin-login | admin.login.destroy | AdminLoginController@destroy | 管理ログアウト | 
Controllerクラス生成
以下、コマンドでAdminLoginControllerクラスを生成します。
php artisan make:controller AdminLoginController
Routing
ルート一覧通りにroutes/web.phpにルートを追記しましょう。
routes/web.php
以下を追記しましょう。
// 管理ログイン画面
Route::get('/admin-login', [AdminLoginController::class, 'create'])->name('admin.login');
// 管理ログイン
Route::post('/admin-login', [AdminLoginController::class, 'store'])->name('admin.login.store');
// 管理ログアウト
Route::delete('/admin-login', [AdminLoginController::class, 'destroy'])->name('admin.login.destroy');
// 管理ログイン後のみアクセス可
Route::middleware('auth:admin')->group(function () {
    Route::get('/admin', function () {
        return view('admin.top');
    })->name('admin.top');
});
追記が終わったら、正常に設定できているか確認しましょう。
php artisan route:list
Middlewareクラス
ゲストアクセス時(ログインしていない場合)に、管理ログイン画面にリダイレクトするように追記します。
app/Http/Middleware/Authenticate.php (Laravel10の場合)
 <?php
 
 namespace App\Http\Middleware;
 
 use Illuminate\Auth\Middleware\Authenticate as Middleware;
 use Illuminate\Http\Request;
 
 class Authenticate extends Middleware
 {
     /**
      * Get the path the user should be redirected to when they are not authenticated.
      */
     protected function redirectTo(Request $request): ?string
     {
+        if (request()->routeIs('admin.*')) {
+            return $request->expectsJson() ? null : route('admin.login');
+        }
         return $request->expectsJson() ? null : route('login');
     }
 }
bootstrap/app.php (Laravel11の場合)
 <?php
 
 use Illuminate\Foundation\Application;
 use Illuminate\Foundation\Configuration\Exceptions;
 use Illuminate\Foundation\Configuration\Middleware;
+use Illuminate\Http\Request;
 
 return Application::configure(basePath: dirname(__DIR__))
     ->withRouting(
         web: __DIR__.'/../routes/web.php',
         commands: __DIR__.'/../routes/console.php',
         health: '/up',
     )
     ->withMiddleware(function (Middleware $middleware) {
         //
+        $middleware->redirectGuestsTo(function(Request $request) {
+            if (request()->routeIs('admin.*')) {
+                return $request->expectsJson() ? null : route('admin.login');
+            }
+            return $request->expectsJson() ? null : route('auth');
+        });
     })
     ->withExceptions(function (Exceptions $exceptions) {
         //
     })->create();
Controllerクラス
AdminLoginControllerクラスにcreate、store、destroyメソッドを追加しましょう。
app/Http/Controllers/AdminLoginController.php
 <?php
 
 namespace App\Http\Controllers;
 
+use App\Http\Requests\AdminLoginRequest;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\View\View;
 
 class AdminLoginController extends Controller
 {
-    //
+    /**
+     * ログイン画面
+     */
+    public function create(): View
+    {
+        return view('admin.login');
+    }
+
+    /**
+     * ログイン
+     */
+    public function store(AdminLoginRequest $request): RedirectResponse
+    {
+        $request->authenticate();
+
+        $request->session()->regenerate();
+
+        return redirect()->intended(route('admin.top'));
+    }
+
+    /**
+     * ログアウト
+     */
+    public function destroy(Request $request): RedirectResponse
+    {
+        Auth::guard('admin')->logout();
+
+        $request->session()->invalidate();
+
+        $request->session()->regenerateToken();
+
+        return to_route('admin.login');
+    }
 }
readouble.com - Laravel 10.x 認証 - ユーザーを手作業で認証する
readouble.com - Laravel 10.x 認証 - ログアウト
readouble.com - Laravel 10.x ヘルパ - route()
readouble.com - Laravel 10.x ヘルパ - to_route()
参考
スターターキット Laravel Breeze の\App\Http\Controllers\Auth\AuthenticatedSessionControllerクラスを参考にしています。
View
admin.login(admin/login.blade.php)と
admin.top(admin/top.blade.php)を追加しましょう。
resources/views/admin/login.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>管理</title>
        <style>
            html,
            body {
                height: 100%;
            }
            body {
                margin: 0;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            form {
                text-align: right;
            }
        </style>
    </head>
    <body>
        <main>
            <form method="POST" action="{{ route('admin.login.store') }}">
                @csrf
                <div>
                    <label for="name">Name: </label>
                    <input type="text" id="name" name="name" required />
                </div>
                <div>
                    <label for="password">Password: </label>
                    <input type="password" id="password" name="password" required />
                </div>
                <div>
                    @error('failed')
                        <p style="color:red">{{ $message }}</p>
                    @enderror
                    <button type="submit">ログイン</button>
                </div>
            </form>
        </main>
    </body>
</html>
resources/views/admin/top.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>管理</title>
        <style>
            html,
            body {
                height: 100%;
            }
            body {
                margin: 0;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            form {
                text-align: right;
            }
        </style>
    </head>
    <body>
        <main>
            @auth('admin')
                <p>ログイン中です。</p>
            @endauth
            <form method="POST" action="{{ route('admin.login.destroy') }}">
                @method('DELETE')
                @csrf
                <button type="submit">ログアウト</button>
            </form>
        </main>
    </body>
</html>
動作確認
以下の動作確認をしましょう。
- 管理トップ画面(/admin) にアクセス- 【expected】 管理ログイン画面(/admin-login) にリダイレクトされる
 
- 【expected】 管理ログイン画面(
- 管理ログイン画面(/admin-login) にて存在しない名称を入力して「ログイン」ボタンクリック
 管理ログイン画面(/admin-login) にて誤ったパスワードを入力して「ログイン」ボタンクリック- 【expected】 管理ログイン画面のログインボタンの上に「These credentials do not match our records.」のメッセージが表示される
 (日本語の場合、「ログイン情報が登録されていません。」)
 
- 【expected】 管理ログイン画面のログインボタンの上に「
- 管理ログイン画面(/admin-login) にて正しい情報(name: admin_01, password: admin)を入力して「ログイン」ボタンクリック- 【expected】管理トップ画面(/admin) に遷移する
 
- 【expected】管理トップ画面(
- 管理トップ画面(/admin) にて「ログアウト」ボタンクリック- 【expected】管理ログイン画面(/admin-login) に遷移し、再度、管理トップ画面(/admin) にアクセスしても管理ログイン画面にリダイレクトされる
 
- 【expected】管理ログイン画面(



Discussion