50
65

More than 5 years have passed since last update.

Laravel 5.4/5.5 で Multi-Auth を実装する

Last updated at Posted at 2017-07-03

追記: このあと 5.5 にアップデートしましたが多分そのままいい感じに動いてます。


以下の記事がとても良くかかれていて、これに沿って実装するだけで殆どうまくいった。

がしかし、一部 5.4 での記載の違いや追加でやっておきたい変更、改良出来そうな手順があったのでほぼ自分用のメモとして記載。

認証追加前の準備

Laravel プロジェクトのセットアップ

composer create-project --prefer-dist laravel/laravel multiauthsample

を行うとデフォルトで入っている User Model がある前提。また Auth scaffold を作成しておく。

php artisan make:auth

追加認証用の Model

追加認証に用いる Model を作成する。

php artisan make:model Admin -m

-m オプションでマイグレーションファイルも同時に作成出来る。この Admin モデルの実装とマイグレーションの内容、つまり Model の構成は User Model と同様に実装しておく。

認証設定

config/auth.php を以下のように修正。

--- a/config/auth.php
+++ b/config/auth.php
@@ -40,6 +40,10 @@ return [
             'driver' => 'session',
             'provider' => 'users',
         ],
+        'admin' => [
+            'driver' => 'session',
+            'provider' => 'admins',
+        ],

         'api' => [
             'driver' => 'token',
@@ -69,6 +73,10 @@ return [
             'driver' => 'eloquent',
             'model' => App\User::class,
         ],
+        'admins' => [
+            'driver' => 'eloquent',
+            'model' => App\Admin::class,
+        ],

         // 'users' => [
         //     'driver' => 'database',
@@ -97,6 +105,11 @@ return [
             'table' => 'password_resets',
             'expire' => 60,
         ],
+        'admins' => [
+            'provider' => 'admins',
+            'table' => 'password_resets',
+            'expire' => 60,
+        ],
     ],

 ];

'guards' 内の要素について、この項目は実装内で guard インスタンスを取得する際 Auth::guard('admin'); の指定に用いるので、名称が異なる同じ内容が複数存在していても問題無い。
'guards' 内に元々ある 'web' が User 用の認証に該当している。参考にした記事では同じ内容を 'user' として追加していて、分かり易くて良さそうだった。

ログイン・ログアウト実装

Auth scaffold 内にはログイン・ログアウト、ユーザー登録、パスワードリマインダの機能が入っているが、最低限のログイン・ログアウトのみ実装。ユースケースにもよるが、管理者的な権限の認証を実装する場合は Web に新規登録画面を設けるよりは artisan タスク等によってユーザーを発行してしまってもいいのではないかと思う。

app/Http/Controllers/Auth/LoginController.phpapp/Http/Controllers/Admin/Auth/LoginController.php としてコピーし、以下のように実装。Auth/ 内の他のファイルはログイン・ログアウトのみ実装の場合は不要。

diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Admin/Auth/LoginController.php
index b2ea669..ae4d412 100644
--- a/app/Http/Controllers/Auth/LoginController.php
+++ b/app/Http/Controllers/Admin/Auth/LoginController.php
@@ -1,10 +1,15 @@
 <?php

-namespace App\Http\Controllers\Auth;
+namespace App\Http\Controllers\Admin\Auth;

 use App\Http\Controllers\Controller;
 use Illuminate\Foundation\Auth\AuthenticatesUsers;

+use Auth;
+
+/**
+ * From app/Http/Controllers/Auth/LoginController.php
+ */
 class LoginController extends Controller
 {
     /*
@@ -25,7 +30,7 @@ class LoginController extends Controller
      *
      * @var string
      */
-    protected $redirectTo = '/home';
+    protected $redirectTo = '/admin/home';

     /**
      * Create a new controller instance.
@@ -34,6 +39,18 @@ class LoginController extends Controller
      */
     public function __construct()
     {
-        $this->middleware('guest')->except('logout');
+        $this->middleware('guest:admin')->except('logout');
+    }
+
+    // http://www.84kure.com/blog/2016/06/06/laravel-5-3%E3%81%A7multi-auth%E3%82%92%E4%BD%BF%E3%81%86/
+
+    public function showLoginForm()
+    {
+        return view('admin.auth.login'); //管理者ログインページのテンプレート
+    }
+
+    protected function guard()
+    {
+        return Auth::guard('admin'); //管理者認証のguardを指定
     }
 }

後述するログイン済ユーザーのリダイレクトに対応する為に $this->middleware('guest:admin')->except('logout'); のように guest Middleware に引数 admin を渡しておく。そして管理者ログインページ用のテンプレートを上記 view() で指定した先に作成する。基本的には resources/views/auth/login.blade.php のコピーで問題無いが、form の method、action 属性は確認する。

ログイン済ユーザーのリダイレクト

追加認証でのログイン済ユーザーがログインページにアクセスした場合、guest Middleware による処理で一括で '/home' にリダイレクトされる。 app/Http/Kernel.php によると guest Middleware の実装は app/Http/Middleware/RedirectIfAuthenticated.php なのでこれを修正する。

diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Mi
index e4cec9c..719e3a5 100644
--- a/app/Http/Middleware/RedirectIfAuthenticated.php
+++ b/app/Http/Middleware/RedirectIfAuthenticated.php
@@ -17,8 +17,18 @@ class RedirectIfAuthenticated
      */
     public function handle($request, Closure $next, $guard = null)
     {
+        $redir = '/home';
+        switch ($guard) {
+        case "admin":
+            $redir = '/admin/home';
+            break;
+        default:
+            $redir = '/home';
+            break;
+        }
+
         if (Auth::guard($guard)->check()) {
-            return redirect('/home');
+            return redirect($redir);
         }

         return $next($request);

元々 $guard という引数を取る作りになっていたので、これによってリダイレクト先を変えるようにする。

未認証時のリダイレクト

認証が必要なページに未認証でアクセスした場合のリダイレクト。

diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index a747e31..ddf4adc 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -59,6 +59,11 @@ class Handler extends ExceptionHandler
         if ($request->expectsJson()) {
             return response()->json(['error' => 'Unauthenticated.'], 401)
         }
+
+        if (in_array('admin', $exception->guards())) {
+            return redirect()->guest(route('admin::login'));
+        }

         return redirect()->guest(route('login'));
     }

元からある記載が名前付 route を使用していたので統一して名前付 route route('admin::login') を使用。'admin/login' 等パス直書きでも問題無い。

ルーティング

5.4 ではルーティング用のファイルの構成が変わっている。routes/web.php に以下の通り追加。

diff --git a/routes/web.php b/routes/web.php
index ca66ae6..850ff5f 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -19,3 +19,24 @@ Auth::routes();

 Route::get('/home', 'HomeController@index')->name('home');
+
+Route::prefix('admin')->name('admin::')->group(function() {
+    // For admin auth pages as generated by Auth::routes();
+    // See also: https://stackoverflow.com/a/39197278/4821980
+
+    // Authentication Routes...
+    Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
+    Route::post('login', 'Auth\LoginController@login');
+    Route::post('logout', 'Auth\LoginController@logout')->name('logout');
+
+    // // Registration Routes...
+    // Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
+    // Route::post('register', 'Auth\RegisterController@register');
+
+    // // Password Reset Routes...
+    // Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
+    // Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
+    // Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
+    // Route::post('password/reset', 'Auth\ResetPasswordController@reset');
+
+    Route::get('/', 'AdminController@index');
+    Route::get('/home', 'AdminController@index');
+});

前項で使用した名前付 route 用の prefix admin:: をグループに指定しておく。

これらの route 定義がどこから来ているかというと、この差分よりもう少し上に元々定義されている Auth::routes(); の実装にある。これが実行されると標準 Auth 用のルート定義が一括で追加されるようになっている。その内容を参考に admin 用に変更し、上記の通り実装する。ユーザー登録やパスワードリマインダを実装する場合はコメントアウトしている部分を元に追加することになる。

認証要件の追加

Controller に追加する場合はコンストラクタへ。

AdminController.php
public function __construct()
{
    $this->middleware('auth:admin');
}

ルーティンググループでも適用出来る。

routes/web.php
Route::middleware('auth:admin')->group(function () {
    Route::get('/admin/home', 'AdminController@index');
});
50
65
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
65