今回はユーザーと管理者の認証を分ける方法とマルチ認証というやり方があるようなのですが、他のサイトを確認しながらやってみたのですが少しうまく行かなかったのでまとめてみました。
今回対応するのは、
- 認証をユーザーと管理者で分ける
- 管理画面は /admin/以下に配置する
- アカウントと登録の2つを実装する(パスワードリセット、メール認証はなし)
- ユーザーと管理者で管理するセッションを分ける
環境:
OS : macOS Mojave 10.14.2MAMP : 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モデル
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
- namespaceにAdminを追加
- redirectToにログイン後のリダイレクト先を設定
- ログイン画面で管理者用のテンプレートを呼び出すように設定
- middlewareの設定を削除し、routeで管理するように変更
- guardをadminに設定
- 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
- namespaceにAdminを追加
- redirectToに管理者登録後のリダイレクト先を設定する
- 登録画面に管理者用のテンプレートを指定するように設定する
- バリデーションでメールアドレスをadminsテーブルでユニークになるように設定
- 作成するモデルAdminに変更する
- 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
- namespaceにAdminを追加する
- 管理者用のテンプレートを設定する
<?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に変更します。
その他
- adminのレイアウトを指定
- route()の指定でadminに変更
その他
ルーティングの設定
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に管理画面でエラーになったときにリダイレクト先を指定します。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;
最後に
以上、ユーザと管理画面の認証を分けることが完了しました。他にもパスワードリセットやメール認証ができていませんが同じように対応したらいけると思います。
参考になったサイト
https://qiita.com/LowSE01/items/ffa256439f665740cc8f