マルチ認証一覧

Laravelでマルチ認証(マルチログイン)を実装する

Laravel5でマルチ認証(adminとuser)を実装します。

一般の会員制のサイトなどでは、user(ユーザー)のログインページだけでなく、admin(管理者)のログインページもあるのが一般的だと思います。

Laravel5の環境構築については、下記のリンクを参照してください。

LAMP環境やLEMP環境にPHPフレームワーク Laravel5.5・Laravel5.6をインストールする方法について紹介しています。 Laravel5.5・Laravel5.6をインストールする際の参考にしてください。

今回は、以前の記事の続きです。下記の記事を参照して、まず、ユーザーのログイン機能を作成してください。

Laravel5.5でログイン機能を実装して見ました。Laravel5では、ログイン機能はartisanコマンド1つで実装することが出来ます。Laravel5でログイン機能を実装しようと考えている方は、参考にしてください。

環境

  • CentOS 7.4
    • PHP 7.2.7
    • MySQL 5.7.22
    • Apache 2.4.6
  • Laravel 5.5/5.6

今回は、ユーザーはuserテーブル、アドミンはadminテーブルを作成し、マルチログインの機能を作成していきます。

adminsテーブルとAdminモデルの作成

まず、最初にadminsテーブルとAdminモデルを作成します。

php artisan make:migration create_admins_table

Laravelのルートディレクトリで上記のコマンドをうち、マイグレーションファイルを作成します。

[root@localhost migrations]# pwd
/laravel_root_directory/database/migrations

上記の場所に作成された、マイグレーションファイルを編集します。
今回は、usersテーブルと同じカラム構造のマイグレーションファイルを作成します。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAdminsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

php artisan migrate を実行してadminsテーブルを作成します。

php artisan migrate

MysSQLのテーブルを見ると、adminsテーブルば作成されていることが確認できます。

mysql> show tables;
+-------------------+
| Tables_in_laravel |
+-------------------+
| admins            |
| migrations        |
| password_resets   |
| users             |
+-------------------+
4 rows in set (0.00 sec)

次に、Adminモデルを作成します。Laravelのルートディレクトリで下記のコマンドを打ってください。

php artisan make:model Admin

app配下にある、Adminモデルのファイルを下記の様に編集します。Userモデルをコピーして、クラス名をUserをAdminに変更しても編集しても大丈夫です。

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
    use Notifiable;
    protected $guard = 'admin';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

auth.phpの編集adminログイン画面へのリダイレクト先追加

auth.phpにadmin関連の設定を追加します。

    'defaults' => [
                 // webからuserに変更
        'guard' => 'user',
        'passwords' => 'users',
    ],

    ...

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        //ここから
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
        'admin-api' => [
            'driver' => 'token',
            'provider' => 'admins',
        ],
               //ここまでを追加
    ],

    ...


    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        //ここから
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
        ],
                //ここまでを追加

   ...


    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
        //ここから
        'admins' => [
            'provider' => 'admins',
            'table' => 'password_resets',
            'expire' => 15,
        ],
                //ここまでを追加
    ],

ユーザーのguardは最初から入っている’web’を使っても問題ないですが、使用箇所でユーザー向けということが明示的に分かるようにuserへ変更しています。

App/Exceptions/Handler.phpを開き下記のように編集

App/Exceptions/Handler.phpを開き、下記の内容に書き換えます。

<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        \Illuminate\Auth\AuthenticationException::class,
        \Illuminate\Auth\Access\AuthorizationException::class,
        \Symfony\Component\HttpKernel\Exception\HttpException::class,
        \Illuminate\Database\Eloquent\ModelNotFoundException::class,
        \Illuminate\Session\TokenMismatchException::class,
        \Illuminate\Validation\ValidationException::class,
    ];
    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }
    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        return parent::render($request, $exception);
    }
    /**
     * Convert an authentication exception into an unauthenticated response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Auth\AuthenticationException  $exception
     * @return \Illuminate\Http\Response
     */
    protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }
        $guard = array_get($exception->guards(), 0);
        switch ($guard) {
          case 'admin':
            $login = 'admin.login';
            break;
          default:
            $login = 'login';
            break;
        }
        return redirect()->guest(route($login));
    }
}

app/Http/Middleware/RedirectIfAuthenticated.phpの内容を書き換える

次に、app/Http/Middleware/RedirectIfAuthenticated.phpの内容を下記の様に書き換えます。

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
      switch ($guard) {
        case 'admin':
          if (Auth::guard($guard)->check()) {
            return redirect()->route('admin.dashboard');
          }
          break;
        default:
          if (Auth::guard($guard)->check()) {
              return redirect('/home');
          }
          break;
      }
      return $next($request);
    }
}

Adminコントローラを作成する

/app/Http/Controllers/AdminController.phpにコントローラを下記の様に作成します。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth:admin');
    }
    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('admin');
    }
}

AdminLoginControllerを作成する

php artisan make:controller auth/AdminLoginController

のartisanコマンドを打つか、下記のソースをコピーしてapp/Http/Controllers/Auth/AdminLoginController.phpを作成し、Adminログインコントローラを作成します。

<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Auth;
class AdminLoginController extends Controller
{
    public function __construct()
    {
      $this->middleware('guest:admin');
    }
    public function showLoginForm()
    {
      return view('auth.admin-login');
    }
    public function login(Request $request)
    {
      // Validate the form data
      $this->validate($request, [
        'email'   => 'required|email',
        'password' => 'required|min:6'
      ]);
      // Attempt to log the user in
      if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->remember)) {
        // if successful, then redirect to their intended location
        return redirect()->intended(route('admin.dashboard'));
      }
      // if unsuccessful, then redirect back to the login with the form data
      return redirect()->back()->withInput($request->only('email', 'remember'));
    }
}

routes/web.phpの修正

routes/web.phpに下記の内容を追加します。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');
//ここから
Route::prefix('admin')->group(function() {
    Route::get('/login', 'Auth\AdminLoginController@showLoginForm')->name('admin.login');
    Route::post('/login', 'Auth\AdminLoginController@login')->name('admin.login.submit');
    Route::get('/', 'AdminController@index')->name('admin.dashboard');
  });
//ここまで

home.blade.phpをコピーして、admin.blade.phpを作成する

home.blade.phpをコピーして、admin.blade.phpを作成します。

/resources/viewsで下記コマンドを。

$ cp home.blade.php admin.blade.php

views/auth/admin-login.blade.phpの作成

views/auth/admin-login.blade.phpに下記の内容のファイルを作成します。

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">ADMIN Login</div>
                <div class="panel-body">
                    <form class="form-horizontal" role="form" method="POST" action="{{ route('admin.login.submit') }}">
                        {{ csrf_field() }}

                        <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                            <label for="email" class="col-md-4 control-label">E-Mail Address</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required autofocus>

                                @if ($errors->has('email'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('email') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
                            <label for="password" class="col-md-4 control-label">Password</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control" name="password" required>

                                @if ($errors->has('password'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('password') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <div class="checkbox">
                                    <label>
                                        <input type="checkbox" name="remember" {{ old('remember') ? 'checked' : '' }}> Remember Me
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-8 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                    Login
                                </button>

                                <a class="btn btn-link" href="{{ route('password.request') }}">
                                    Forgot Your Password?
                                </a>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

これで、Laravel5.5でのマルチログインは、完成です。
管理者ログインのページは、メールでのパスワードの再発行をせず、管理者がロールなどを設定して、アカウントを発行することなどが多いと思い、今回はパスワードリマインダーの機能の作成までは、していません。

また、最初のアカウントの発行は、tinkerを使用すると、簡単に認証アカウントを発行することが出来ます。

php artisan tinker

のコマンドを打つと、Laravelのプログラムを書くことが出来、普通にLaravelのソースを書くことでアカウントを発行することが出来ます。ぜひ、試してみてください。
作成したアカウントで、”http://ip-address/admin/login”にアクセスして認証をするとログインできます。

今回は、Laravel5.5の環境でマルチログインの機能を作成してみました。Laravel5.5の環境でマルチログインを実装しようとしている人の参考になると嬉しいです。