Laravel5.6 ドメインルーティング＋マルチ認証

# Laravel5.6 ドメインルーティング＋マルチ認証

いじってる間にLaravel5.7が出てしまったけど5.6です。多分変わってないはず。

## 前提条件

* Laravel5.6をcomposerでインストール済み
    `composer create-project --prefer-dist laravel/laravel myapp "5.6.*"`
* DocumentRootはpublicディレクトリを指定。その他AllowOverride All等も適宜設定済み
    `DocumentRoot "/var/www/html/myapp/public"`
* データベースを利用できるよう設定済み

## やりたいこと

* 接続時のドメインでフロントエンドとバックエンドに振り分ける
* フロントエンドとバックエンドで異なるDBテーブルのユーザーで認証する
* ログインIDはメールアドレスではなく独自で発行したものを使用する

※ユーザー登録、パスワードリセットは取り扱わない

## マイグレーション

```bash
php artisan make:migration create_members_table
```

```php:database/migrations/9999_99_99_create_members_table.php

    public function up()
    {
        Schema::create('members', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('loginid')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

```

create_admins_tableも同様に作成する。

マイグレーションを実行する

`php artisan migrate`

## Eloquentモデルを作成

`Illuminate\Database\Eloquent\Model`ではなく`Illuminate\Foundation\Auth\User`を継承する。

```php:app/Member.php
<?php
namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class Member extends Authenticatable
{
    /** @var array */
    protected $fillable = [
        'name', 'loginid', 'password',
    ];

    /** @var array */
    protected $hidden = [
        'password', 'remember_token',
    ];
}
```

app/Admin.phpも同様に作成する。

## ユーザーデータ作成

`php artisan tinker` 等で作成しておく。

```php
>>> $member = App\Member::create(['name' => 'Tester', 'loginid' => 'tester', 'password' => Hash::make('password')]);
>>> $admin = App\Admin::create(['name' => 'Admin', 'loginid' => 'admin', 'password' => Hash::make('password')]);
```

## 認証設定

ガードとプロバイダーにフロントエンド用、バックエンド用を設定する。

```php:config/auth.php
<?php

return [
    'defaults' => [
        'guard' => 'front',  // webは使用しないのでfrontに変更
        'passwords' => 'users',
    ],
    
    'guards' => [  // webを削除してfrontとbackを追加
        'front' => [
            'driver' => 'session',
            'provider' => 'members',
        ],

        'back' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],

    ],

    'providers' => [  // 作成したEloquentモデルを指定
        'members' => [
            'driver' => 'eloquent',
            'model' => App\Member::class,
        ],

        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],
    ],

```

## ルーティング

ドメインでルーティングを分ける。フロントエンドとバックエンドのドメインを変えることでセッションCookieの競合も解決出来るので一石二鳥。
ここではlocalhost => App\Http\Controllers\Front, 127.0.0.1 => App\Http\Controllers\Back以下のコントローラーを利用するようにする。

```php:routes/web.php

Route::group(['domain' => 'localhost', 'namespace' => 'Front'], function () {
    Route::get('/login', 'Auth\LoginController@showLoginForm');
    Route::post('/login', 'Auth\LoginController@login');
    Route::post('/logout', 'Auth\LoginController@logout');

    Route::middleware(['auth:front'])->group(function () {
        // 認証が必要な画面はこの下に記述
        Route::get('/home', 'HomeController@index');
    });
});

Route::group(['domain' => '127.0.0.1', 'namespace' => 'Back'], function () {
    Route::get('/login', 'Auth\LoginController@showLoginForm');
    Route::post('/login', 'Auth\LoginController@login');
    Route::post('/logout', 'Auth\LoginController@logout');

    Route::middleware(['auth:back'])->group(function () {
        // 認証が必要な画面はこの下に記述
        Route::get('/home', 'HomeController@index');
    });
});

```

## 未認証時のリダイレクト先を設定

デフォルトは`route('login')`にリダイレクトするようになっているので調整する。

Handler->unauthenticatedを追加する。
バックエンドはログイン画面に、フロントエンドはトップにリダイレクトする。

```php:app/Exceptions/Handler.php

    protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['message' => $exception->getMessage()], 401);
        }
        if (in_array('back', $exception->guards())) {
            return redirect()->guest(url('/login'));
        }
        if (in_array('front', $exception->guards())) {
            return redirect()->guest(url('/'));
        }
        return redirect()->guest(url('/'));
    }

```

## コントローラーの作成

ログイン画面

```php:app/Http/Controllers/Front/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Front\Auth;

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

class LoginController extends Controller
{
    use AuthenticatesUsers;

    /**
     * ログイン後に遷移する画面を設定する
     *
     * @var string
     */
    protected $redirectTo = '/home';

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    /**
     * ログインIDに使用する項目を設定する
     *
     * @return string
     */
    public function username()
    {
        return 'loginid';
    }

    /**
     * 認証に使用するguardを設定する
     *
     * @return mixed
     */
    public function guard()
    {
        return Auth::guard('front');
    }
}
```

ログイン後に表示する画面

```php:app/Http/Controllers/Front/HomeController.php
<?php

namespace App\Http\Controllers\Front;

use App\Http\Controllers\Controller;

class HomeController extends Controller
{
    public function index()
    {
        return view('home');
    }
}
```

Backも同様に作成する。

## ビューの作成

`php artisan make:auth`を実行した際に作成されるものを参考に適宜作成。

`route('login')`等の名前付きルートURLは使用せず、`url('/login')`等に置き換える。
※テンプレートを共用するための措置。分けるのであればルート側で適宜名前を付けてもよい。

```php:resources/layouts/app.blade.php
内容は割愛
```

```php:resources/views/auth/login.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ url('/login') }}" aria-label="{{ __('Login') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="email" class="col-sm-4 col-form-label text-md-right">{{ __('Login ID') }}</label>

                            <div class="col-md-6">
                                <input id="loginid" type="text" class="form-control{{ $errors->has('loginid') ? ' is-invalid' : '' }}" name="loginid" value="{{ old('loginid') }}" required autofocus>

                                @if ($errors->has('loginid'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('loginid') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                                @if ($errors->has('password'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('password') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

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

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

```php:resources/views/home.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Dashboard</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    You are logged in!
                    @auth('front')
                        Front
                    @endauth
                    @auth('back')
                        Back
                    @endauth
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

## 確認
http://localhost/login にフロントエンド用のユーザーで、
http://127.0.0.1/login にバックエンド用のユーザーでログインでき、
逆にしてエラーになればOK。