Laravel 5.7 マルチ認証で管理者とユーザーの認証を分ける

Laravel

今回はユーザーと管理者の認証を分ける方法とマルチ認証というやり方があるようなのですが、他のサイトを確認しながらやってみたのですが少しうまく行かなかったのでまとめてみました。

今回対応するのは、

  • 認証をユーザーと管理者で分ける
  • 管理画面は /admin/以下に配置する
  • アカウントと登録の2つを実装する(パスワードリセット、メール認証はなし)
  • ユーザーと管理者で管理するセッションを分ける

環境:

OS : macOS Mojave 10.14.2

MAMP : 5.1

Laravel : 5.7.6

ユーザ認証までを作成する

Laraveのプロジェクトを作成する

composerやMAMPなどLaravelの環境については「Laravel開発:1.環境構築をMAMPを使用して作成する」に記載しているので、こちら参考にして下さい。

sampleという名でプロジェクトを作成します。

$ composer create-project laravel/laravel --prefer-dist sample
$ cd sample
$ chmod -R 777 storage
$ chmod -R bootstrap/cache

・VirtualHostの設定

MAMPを利用している場合は MAMP/con/apache/extra/httpd-vhosts.conf を編集します。

・DBの設定

phpMyAdminから今回のプロジェクト用に「sample」という名でdatabaseを作成します。

・.envの設定

以下の値を変更して下さい。

APP_URL=http://localhost:8888
DB_PORT=8889
DB_DATABASE=sample
DB_USERNAME=root
DB_PASSWORD=root

この時点で http://localhost:8888/ を表示するとLaravelの画面が表示されます。

認証機能の追加

ユーザの認証機能を追加します。

$ php artisan make:auth

こちらのコマンドに認証に必要な機能が用意されます。

実際にマイグレーションをするとテーブルが作成されるのでユーザ認証の実装は完成となります。

管理者認証を追加する

引き続き管理者の認証を作成していきます。

モデルとマイグレーションファイルの作成

$ php artisan make:model Admin -m

こちらのコマンドでAdminモデルとマイグレションファイルが作成されます。

ユーザーのファイルを参考に編集していきます。

Adminモデル

app/User.phpを参考にapp/Admin.phpを編集します。User.phpからそのままコピーをしてモデル名だけを変更します。

<?php

namespace App;

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

class Admin extends Authenticatable
{
    use Notifiable;

    /**
     * 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',
    ];
}

マイグレーションファイル

database/migrations内にadmins_tableのマイグレションファイルがあります。

こちらもusers_tableを参考にカラムを設定します。

public function up()
{
    Schema::create('admins', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

マイグレーション

マイグレーションを実行すれば必要なテーブルが作成されます。

php artisan migrate

認証の設定を編集する

config/auth.phpに認証の設定情報があります。

ユーザーの分は既に設定されていますので、管理者用の分を追加していきます。

・デフォルトのguardがwebとなっているので、userに変更します。

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

・管理者のguardを追加します。

'guards' => [
        // webからuserに変更
    'user' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],

    // 追加
    'admin' => [
        'driver' => 'session',
        '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
    ]
],

Controllerの作成

管理者用のディレクトリを作成し、ユーザーのコントローラを利用しながら作成していきます。

app/Http/ControllersにAdminディレクトリを作成します。

そこに、app/Http/Controllers/Authとapp/Http/Controllers/HomeController.phpをコピーします。

app
├── Http
    ├── Controllers
        ├── Admin  // ディレクトリを作成
            ├── Auth // Authディレクトリのコピー
            │   ├── ForgotPasswordController.php
            │   ├── LoginController.php
            │   ├── RegisterController.php
            │   ├── ResetPasswordController.php
            │   └── VerificationController.php
            └── HomeController.php // HomeController.phpのコピー

LoginController

  1. namespaceにAdminを追加
  2. redirectToにログイン後のリダイレクト先を設定
  3. ログイン画面で管理者用のテンプレートを呼び出すように設定
  4. middlewareの設定を削除し、routeで管理するように変更
  5. guardをadminに設定
  6. logout時のリダイレクト先を設定
<?php
namespace App\Http\Controllers\Admin\Auth; // Adminを追加

use App\Http\Controllers\Admin\Auth;   // 追加
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/admin/home';    // ログイン後のリダイレクト先

    // ログイン画面
    public function showLoginForm()
    {
        return view('admin.auth.login'); //管理者ログインページのテンプレート
    }

    protected function guard()
    {
        return \Auth::guard('admin'); //管理者認証のguardを指定
    }

    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->invalidate();

        return $this->loggedOut($request) ?: redirect('/admin/');  // ログアウト後のリダイレクト先
    }
}

RegisterController

  1. namespaceにAdminを追加
  2. redirectToに管理者登録後のリダイレクト先を設定する
  3. 登録画面に管理者用のテンプレートを指定するように設定する
  4. バリデーションでメールアドレスをadminsテーブルでユニークになるように設定
  5. 作成するモデルAdminに変更する
  6. guardをAdminに設定する
<?php
namespace App\Http\Controllers\Admin\Auth;  // Adminの追加

use App\Admin;  // 追加
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/admin/home';  // リダイレクト先


    public function showRegisterForm()
    {
        return view('admin.auth.register');  // 管理者用テンプレート
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:admins'], // adminsテーブに変更
            'password' => ['required', 'string', 'min:6', 'confirmed'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        return Admin::create([  // Adminに変更
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }

    /**
     * Get the guard to be used during registration.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return \Auth::guard('admin'); //管理者認証のguardを指定
    }
}

HomeController

  1. namespaceにAdminを追加する
  2. 管理者用のテンプレートを設定する
<?php
namespace App\Http\Controllers\Admin;  // Adminの追加

use App\Http\Controllers\Controller;   // 追加
use Illuminate\Http\Request;


class HomeController extends Controller
{

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('admin.home');   // 管理者用のテンプレート
    }
}

その他

ForgotPasswordController、ResetPasswordController、VerificationControllerについてはnamepspaseを変更する

(対応時に加筆します)

Viewを追加

管理者用にadminを追加して、ユーザーのviewを参考に作成していきます。

resources/viewsにadminディレクトリを作成する

authディレクトリとhome、welcomeをコピーする

管理者用のレイアウトにlayouts/app.blade.phpをadmin.blade.phpとしてコピーする

views
├── admin
│   ├── auth
│   │   ├── login.blade.php
│   │   ├── passwords
│   │   │   ├── email.blade.php
│   │   │   └── reset.blade.php
│   │   ├── register.blade.php
│   │   └── verify.blade.php
│   ├── home.blade.php
│   └── welcome.blade.php
├── layouts
    ├── admin.blade.php
    └── app.blade.php

layouts/admin

管理者として分かりやすいように背景色を変更します。

<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<style>body{background-color: #26263c;}</style>

管理者のログインチェックができるように変更します。

@if(Auth::guard('admin')->check())
    <li class="nav-item dropdown">
        <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
            {{ Auth::guard('admin')->user()->name }} <span class="caret"></span>
        </a>

        <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
            <a class="dropdown-item" href="{{ route('admin.logout') }}"
               onclick="event.preventDefault();
                             document.getElementById('logout-form').submit();">
                {{ __('Logout') }}
            </a>

            <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;">
                @csrf
            </form>
        </div>
    </li>
@else
    <li class="nav-item">
        <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a>
    </li>
    @if (Route::has('register'))
        <li class="nav-item">
            <a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a>
        </li>
    @endif
@endif

分かりづらいですが、

@guest
・・・・
@endguest

内を変更しています。

Auth::guard(‘admin’)->check()・・・管理者としてログインしているかをチェックします。

Auth:guard(‘admin’)->user()・・・ログインしている管理者情報を取得します。

route()の指定はadminに変更します。

その他

その他のViewは以下の変更をします。

views/admin/home.blade.php,welcom.blade.php

views/admin/auth/login.blade.php,register.blade.php,verify.blade.php

views/admin/auth/passwords/email.blade.php,reset.blade.php

  1. レイアウトを変更 @extends(‘layouts.admin’)
  2. route()の指定でadminに変更 route(‘admin.xxxxx’)

ルーティングの設定

routes/web.phpに管理画面の設定を追加します。

Route::group(['prefix' => 'admin', 'middleware' => 'guest:admin'], function() {
    Route::get('/', function () {
        return view('admin.welcome');
    });
    Route::get('login', 'Admin\Auth\LoginController@showLoginForm')->name('admin.login');

    Route::get('login', 'Admin\Auth\LoginController@showLoginForm')->name('admin.login');
    Route::post('login', 'Admin\Auth\LoginController@login')->name('admin.login');

    Route::get('register', 'Admin\Auth\RegisterController@showRegisterForm')->name('admin.register');
    Route::post('register', 'Admin\Auth\RegisterController@register')->name('admin.register');

    Route::get('password/rest', 'Admin\Auth\ForgotPasswordController@showLinkRequestForm')->name('admin.password.request');
});

Route::group(['prefix' => 'admin', 'middleware' => 'auth:admin'], function(){
    Route::post('logout', 'Admin\Auth\LoginController@logout')->name('admin.logout');
    Route::get('home', 'Admin\HomeController@index')->name('admin.home');
});

groupを利用してまとめて設定をしています。

エラー時の設定

app/Exceptions/Handler.phpに管理画面でエラーになったときにリダイレクト先を指定します。

use Illuminate\Auth\AuthenticationException;  // 追加

// 追加
protected function unauthenticated($request, AuthenticationException $exception)
{
    if ($request->expectsJson()) {
        return response()->json(['error' => 'Unauthenticated.'], 401);
    }
    if (in_array('admin', $exception->guards())) { // ここから
        return redirect()->guest('admin/login');
    } // ここまで追記
    return redirect()->guest(route('login'));
}

セッションの設定

管理者とユーザで管理するセッションを分けるようにします。

.envにキーを設定します。

SESSION_COOKIE=auth
SESSION_COOKIE_ADMIN=auth-admin

config/session.phpでadminのときにキーを設定し直すようにします。

// 配列を一度保持するように書き換えます。
$conf = [
// 元々記載のあった配列の中身
];

// 管理画面のセッションクッキーを変更する
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
if (strpos($uri, '/admin/') === 0 || $uri === '/admin') {
    $conf['cookie'] = env(
        'SESSION_COOKIE_ADMIN',
        str_slug(env('APP_NAME', 'laravel'), '_').'_admin_session'
    );
}
return $conf;

最後に

以上、ユーザと管理画面の認証を分けることが完了しました。

他にもパスワードリセットやメール認証ができていませんが同じように対応したらいけると思います。

参考になったサイト

Laravel 5.3の認証関連機能で新規登録・ログイン・ログアウト後のリダイレクト先を指定する方法 - Qiita
## やりたいこと * php artisan make:authで認証関連機能を生成すると、新規登録・ログイン後は「/home」へ、ログアウト後は「/」へリダイレクトされるが、これを変更したい。 ## 前提 * php ar...
Laravelでマルチ認証(マルチログイン)を実装する
Laravel5でマルチ認証(マルチログイン機能)を実装するする方法について紹介しています。Laravel5.5でマルチログインを実装しようとしている人は、参考にしてください。
Laravel5.4でマルチログインを実装する | Will Style Inc.|神戸にあるウェブ制作会社
こんにちは、今期アニメは本当に久々の豊作で毎週のアニメ消化も忙しくしているデザイナーの奥田です。さて、久しぶりにLaravelを使って開発を行う機会がありました。その際に使った5.2からのデフォルトの機能であるマルチ認証をご説明したいと思い...
Laravel5.4でマルチ認証(userとadmin)を実装する方法
やりたいこと Laravel5.4でuserとadminの2つのログイン認証を実装する。 例えばちょっとした会員制のコミュニティサイトだとして、ユーザー画面での会員ログインと、会員管理などの機能を持つ管理画面への管理者ログインが
ユーザー認証(10)Laravel 5.2 マルチ認証 – ララジャパン

コメント