LoginSignup
19
8

More than 1 year has passed since last update.

Laravel5.5LTS: multi Auth

Last updated at Posted at 2020-10-16

Laravelのマルチ認証の記事はたいていコピペを多用してます。
コピペの多用バグの温床になりやすいので条件分岐で極力減らしてみました。

前提条件: Laravel5.5 インストール

Laravel5.5認証(公式)

####.env(DB設定)

.env
//DB接続設定
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE={データベース名}
DB_USERNAME={user}
DB_PASSWORD={password}

####AppServiceProvider.php

app/Providers/AppServiceProvider.php
//DBの191文字超えエラー回避設定
class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
++      \Illuminate\Support\Facades\Schema::defaultStringLength(191);
    }
}

####Authとマイグレーション

認証のインストールとマイグレーション
//認証のインストール
$ php artisan make:auth
//認証用DBテーブル作成
$ php artisan migrate
//-> DBにusers・password_resetsテーブルが作成されたか確認。

####Adminモデルの生成

Adminモデルとマイグレーションの生成
$ php artisan make:model Admin -m
//-> App/Admin.php
//-> database/migrations/{日付}_create_admins_table.php

####Adminモデル

app/Admin.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable //modify
{
    use Notifiable;
    protected $fillable = [
        'name', 'email', 'password',
    ];
    protected $hidden = [
        'password', 'remember_token',
    ];
}

####Adminテーブルのマイグレーション
create_users_table.phpの内容をコピーして変更

database/migrations/{日付}create_admins_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAdminsTable extends Migration
{
    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();
        });
    }
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

####adminテーブルのマイグレーション実行

$ php artisan migrate
//adminsテーブルが生成されたか確認

####tinker -> adminユーザーの生成
password_hash(公式)

tinker
$ php artisan tinker
>>> $admin = new App\Admin;
>>> $admin->name = 'admin';
>>> $admin->email = 'test@test.com';
>>> $admin->password = password_hash('password', PASSWORD_DEFAULT);
>>> $admin->save();
//phpMyadmin等でユーザー生成の確認

####auth.php(公式)
admin側の定義の追加

config/auth.php
<?php
return [
    'defaults' => [
        'guard' => 'user', //modify
        'passwords' => 'users',
    ],
    'guards' => [
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'user' => [         //modify
            'driver' => 'session',
            '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' => 60,
+        ],
    ],
];

Laravelの認証機能は「ガード」「プロバイダ」を中心概念として構成されています。ガードは各リクエストごとに、どのようにユーザーを認証するかを定義します。たとえば、Laravelにはセッションストレージクッキーを使いながら状態を維持するsessionガードが用意されています。

####ログインしていないときのリダイレクト先

app/Exceptions/Handler.php
<?php
namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException; //add
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    protected $dontReport = [
        //
    ];
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];
    public function report(Exception $exception)
    {
        parent::report($exception);
    }
    public function render($request, Exception $exception)
    {
        return parent::render($request, $exception);
    }
    //未ログイン時 -> ガード属性のログインページにリダイレクト
    public function unauthenticated($request, AuthenticationException $exception)
    {
        if($request->expectsJson()){
            return response()->json(['message' => $exception->getMessage()], 401);
        }
        if (in_array('admin', $exception->guards())) {
            return redirect()->guest(route('admin.login'));
        }
        return redirect()->guest(route('login'));
    }
}

####ルーティング

route/web.php
<?php
Auth::routes();
// User 認証不要
Route::get('/', function () { 
    return redirect('home');
});
// User ログイン後
Route::group(['middleware' => 'auth:user'], function() {
    Route::get('home', 'HomeController@index')->name('home');
});
// Admin 認証不要
Route::group(['prefix' => 'admin'], function() {                                  
    Route::get('login', 'Admin\LoginController@showLoginForm')->name('admin.login');
    Route::post('login', 'Admin\LoginController@login');     
});                                                                                 
// Admin ログイン後
Route::group(['prefix' => 'admin', 'middleware' => 'auth:admin'], function() {
    Route::post('logout', 'Admin\LoginController@logout')->name('admin.logout');
    Route::get('home', 'HomeController@index')->name('admin.home');
});

####コントローラーの構成
adminフォルダを作成してAuthフォルダからLoginController.phpをコピーします。
WS000039.png

####LoginController.php(Auth)

app/Http/Controller/Auth/LoginController
<?php
namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;            //add
use Illuminate\Support\Facades\Auth;    //add

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = '/home';

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
    public function logout(Request $request)
    {
        Auth::guard('user')->logout();  //modify
        return redirect('login');  //modify
    }
}

####LoginController.php(Admin)

app/Http/Controller/Admin/LoginController.php
<?php
namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;            //add
use Illuminate\Support\Facades\Auth;    //add

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = 'admin/home'; //modify

    public function __construct()
    {
        $this->middleware('guest:admin')->except('logout');//modify
    }
    public function showLoginForm()
    {
        //login.blade.phpは共用
        return view('auth.login');  //modify
    }
    protected function guard()
    {
        return Auth::guard('admin');  //modify
    }
    //以下追加
    public function logout(Request $request)
    {
        Auth::guard('admin')->logout();
        return redirect('admin/login');
    }
}

####ログイン後のリダイレクト先

app/Http/Middleware/RedirectIfAuthenticated.php
<?php                                                               
namespace App\Http\Middleware;                                      

use Closure;                                                        
use Illuminate\Http\Request;                                        
use Illuminate\Support\Facades\Auth;                                
                                                                    
class RedirectIfAuthenticated                                       
{                                                                   
    public function handle($request, Closure $next, $guard = null) {
        if (Auth::guard($guard)->check()) {
            switch ($guard) {
                case 'admin':
                    return redirect('/admin/home');
                    break;
                default:
                    return redirect('/home');
            }
        }
        return $next($request);
    }                                                               
}                                                                   

####ヘルパー関数の定義(権限を便利に利用するための関数)
参考:ヘルパ関数の自作

helpers.php
<?php
use Illuminate\Support\Facades\Auth;

//ユーザー情報の取得
if (! function_exists('userInfo')) {
    function userInfo() {
        return Auth::guard()->user();
    }
}
//ログインしているか?
if (! function_exists('isLogin')) {
    function isLogin() {
        return !empty(userInfo());
    }
}
//ユーザーの属性を取得する
if (! function_exists('getUserType')) {
    function getUserType() {
        if (isLogin()) {
            switch (get_class(userInfo())) {
                case 'App\Admin':
                    $userType = 'Admin';
                    break;
                default:
                    $userType = 'User';
            }
        } else {
            $userType = 'Guest';
        }
        return $userType;
    }
}
//現在のページが管理者用か?-> adminのRouteには'admin'のプレフィクスをつける。
if (! function_exists('isAdminRoute')) {
    function isAdminRoute() {
        return strpos(\Route::currentRouteName(), 'admin') !== false;
    }
}
if (! function_exists('isAdminLogin')) {
   function isAdminLogin() {
       return isAdminRoute() && strpos(\Route::currentRouteName(), 'login') !== false;
   }
}

####コントローラーの使用例(条件分岐の例)
HomeController.php

app/Http/Controller/HomeController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;    //add

class HomeController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }
    public function index()
    {
        //string: ユーザー属性を取得してテスト
        $userType = getUserType();
        dd($userType);
        return view('home');
    }
}

####ビュー
getUserType()で属性を判断できます。

WS000041.JPG
ビューはコピペせずに条件分岐でuseradminの制御を行います。

####app.blade.php

resources/views/layouts/app.blade.php
~~~~
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
@if (isAdminRoute())
<style>body{background-color: tomato;}</style>
@endif
~~~~
<!-- Branding Image -->
@if (!isLogin())
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}</a>
@elseif (isLogin() && getUserType() == 'User')
<a class="navbar-brand" href="{{ route('home') }}">
{{ config('app.name', 'Laravel') }}</a>
@elseif (isLogin() && getUserType() == 'Admin')
<a class="navbar-brand" href="{{ route('admin.home') }}">
{{ config('app.name', 'Laravel') }}</a>
@endif
~~~~
<!-- Authentication Links -->
@if (isLogin() && !isAdminLogin())
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true" v-pre>
{{ userInfo()->name }} <span class="caret"></span>
</a>
@elseif (!isLogin())
<li><a href="{{ route('login') }}">Login</a></li>
<li><a href="{{ route('register') }}">Register</a></li>
@elseif (!isLogin() || isAdminLogin())
<li><a href="{{ route('admin.login') }}">Login</a></li>
@endif
@if (isLogin() && getUserType() == 'User')
<ul class="dropdown-menu">
<li>
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">Logout</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
</li>
</ul>
@elseif (isLogin() && getUserType() == 'Admin')
<ul class="dropdown-menu">
<li>
<a 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_field() }}
</form>
</li>
</ul>
@endif
~~~~

####Login.blade.php

resources/views/auth/login.blade.php
~~~~
@if ((!isLogin() && isAdminRoute()) || (getUserType() == 'User' && isAdminLogin()))
<form class="form-horizontal" method="POST" action="{{ route('admin.login') }}">
@elseif (!isLogin() && !isAdminRoute())
<form class="form-horizontal" method="POST" action="{{ route('login') }}">
@endif
{{ csrf_field() }}

####Home.blade.php

resources/views/home.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">
<?php $user = userInfo(); ?>
{{ $user->name . 'さん: ' }}
{{ $user->id . ', ' }}
{{ $user->email }}
{{ '属性: ' . getUserType() }}
</div>
<div class="panel-body">
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
You are logged in!
</div>
</div>
</div>
</div>
</div>
@endsection

いかがでしたでしょうか?
条件分岐のやり方を押さえることができれば改変・応用は簡単です。
少なくとも1つ機能を増やすためにファイル間を移動することは確実に減るでしょう。
改修デバッグ作業も同様です。
管理権限を増やすのも楽だと思います。

LGTMお願いします!
ストックのついでにお願いします!
モチベーションがあがります!

補足
オブジェクト指向を学び始めてから、この記事は中途半端だったなあと思います笑
1.UIはBaseを継承させる
2.Factoryで継承済みUIを自動でチョイスさせる

UI内でIfを使いまくってる部分を直したいw

19
8
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
19
8