multi authで2段階認証も設定できたと思ったが、途中からUSERにリンクされていただけだった。
2段階認証はできないが、jetstreamでの認証をマルチにすることを記録。
profileを複製できれば、二段階認証も可能なはず。profile画像はデータベースに直接入れると反映されるから、、、
Table
管理者情報を保存する必要がある。
Modelとmigrationを作成してmigrateする。
Users Modelを参照して作る。
作成するModelは仮にAdminという名前とする。
php artisan make:model Admin -m
で作成してよいが、特別なmodelなので、USERモデルを参考にすること。
use Illuminate\Foundation\Auth\User as Authenticatable;が必要になるし、
class User extends Authenticatable
と普通のModelを継承するわけではないので注意。
Responseクラスの作成
ログイン後の動きなどを制御?
AppフォルダーにResponsesフォルダーを新規に作成して、vendor/laravel/fortify/src/Http/Responses/LoginResponse.php
を参照してAdminLoginResponseを作成。
ログイン View
login.blade.phpをそのまま利用するが、guardで行先を分ける?
<!-- guard変数がセットされているかによってPOST先を変更 -->
<form method="POST" action="{{ isset($guard) ? route('admin.login') : route('login') }}">
AttemptToAuthenticateクラス
認証処理。
App/Actions/に新規にAdminフォルダーを作成し、AttemptToAuthenticate.php作成。
vendor/laravel/fortify/src/Actions/AttemptToAuthenticate.phpを参照する。
AdminLoginServiceProviderクラスの作成
guardをDIするのに必要なクラス。
php artisan make:provider AdminLoginServiceProvider
<?php
namespace App\Providers;
use App\Http\Controllers\Auth\LoginController;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Support\Facades\Auth;
use App\Actions\Admin\AttemptToAuthenticate;
use Illuminate\Support\ServiceProvider;
class AdminLoginServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app
->when([LoginController::class, AttemptToAuthenticate::class])
->needs(StatefulGuard::class)
->give(function () {
return Auth::guard('admin');
});
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
config\app.phpを修正
AdminLoginServiceProviderが反映されるようにapp.phpを修正
App\Providers\JetstreamServiceProvider::class,
App\Providers\AdminLoginServiceProvider::class, // この行を追加
認証用のControllerを作成
php artisan make:controller Auth/LoginController
Authフォルダーに入れることにする。
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Http\Request;
use Illuminate\Routing\Pipeline;
use App\Actions\Admin\AttemptToAuthenticate;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
use App\Responses\AdminLoginResponse;
use Laravel\Fortify\Contracts\LogoutResponse;
use Laravel\Fortify\Http\Requests\LoginRequest;
class LoginController extends Controller
{
/**
* The guard implementation.
*
* @var \Illuminate\Contracts\Auth\StatefulGuard
*/
protected $guard;
/**
* Create a new controller instance.
*
* @param \Illuminate\Contracts\Auth\StatefulGuard
* @return void
*/
public function __construct(StatefulGuard $guard)
{
$this->guard = $guard;
}
/**
* Show the login view.
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function create()
{
return view('auth.login', ['guard' => 'admin']);
}
/**
* Attempt to authenticate a new session.
*
* @param \Laravel\Fortify\Http\Requests\LoginRequest $request
* @return mixed
*/
public function store(LoginRequest $request)
{
return $this->loginPipeline($request)->then(function ($request) {
return app(AdminLoginResponse::class);
});
}
/**
* Get the authentication pipeline instance.
*
* @param \Laravel\Fortify\Http\Requests\LoginRequest $request
* @return \Illuminate\Pipeline\Pipeline
*/
protected function loginPipeline(LoginRequest $request)
{
return (new Pipeline(app()))->send($request)->through(array_filter([
AttemptToAuthenticate::class,
PrepareAuthenticatedSession::class,
]));
}
/**
* Destroy an authenticated session.
*
* @param \Illuminate\Http\Request $request
* @return \Laravel\Fortify\Contracts\LogoutResponse
*/
public function destroy(Request $request): LogoutResponse
{
$this->guard->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return app(LogoutResponse::class);
}
}
ログイン後のダッシュボード作成
php artisan make:controller AdminDashboardController
コントローラとbladeの作成
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminDashboardController extends Controller
{
public function index()
{
return view('admin.dashboard');
}
}
resources/views/admin/dashboard.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<!-- わかりやすいようにここを修正-->
管理者ダッシュボード
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<x-jet-welcome />
</div>
</div>
</div>
</x-app-layout>
MiddleWare ログインが必要なページ
User、Adminともに認証が必要なページ(今回はdashboard, admin/dashboard)にアクセスした場合、それぞれのログインページにリダイレクトさせる。
app/Http/Middleware/Authenticate.php
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (!$request->expectsJson()) {
if ($request->is('admin/*')) {
return route('admin.login');
}
return route('login');
}
}
}
ルーティング
web.phpの修正
<?php
use App\Http\Controllers\AdminDashboardController;
use App\Http\Controllers\Auth\LoginController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| 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');
});
Route::prefix('admin')->group(function () {
Route::get('/', function () {
if(Route::middleware('auth:admin')){
return redirect()->route('admin.dashboard');
}else{
return redirect()->route('admin.login');
}
});
Route::get('login', [LoginController::class, 'create'])->name('admin.login');
Route::post('login', [LoginController::class, 'store']);
Route::middleware('auth:admin')->group(function () {
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('admin.dashboard');
});
});
Route::middleware(['auth:web', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
認証ガードであるadminを定義
config/auth.phpの修正
~~省略~~
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
~~省略~~
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
~~省略~~
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
これでログイン画面はできる。Jetのデザイン。
しかしながら、ヘッダーなど、リンクがあるところはすべてUSERモードのものなので注意が必要。
ログアウト後の挙動とか、二段階認証以前にいろいろと手を加えないといけない。
この説明では登録ができなかったので、追加。
登録コントローラー追加
AuthにRegisteredUserController追加。
php artisan make:controller Auth/RegisteredUserController
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Hash;
use Validator;
use App\Models\Admin;
use Auth;
class RegisteredUserController extends Controller {
/**
* The guard implementation.
*
* @var \Illuminate\Contracts\Auth\StatefulGuard
*/
protected $guard;
/**
* Create a new controller instance.
*
* @param \Illuminate\Contracts\Auth\StatefulGuard $guard
* @return void
*/
public function __construct(StatefulGuard $guard) {
$this->guard = $guard;
}
/**
* Show the registration view.
*
* @param \Illuminate\Http\Request $request
* @return \Laravel\Fortify\Contracts\RegisterViewResponse
*/
public function create(Request $request) {
if (Auth::guard('admin')->user()) {
return redirect('admin/dashboard');
} else {
return view('admin/register');
}
}
/**
* Create a new registered user.
*
* @param \Illuminate\Http\Request $request
* @param \Laravel\Fortify\Contracts\CreatesNewUsers $creator
* @return \Laravel\Fortify\Contracts\RegisterResponse
*/
public function store(Request $request) {
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required',
'password' => 'required|confirmed',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$admin = Admin::create([
'name' => $request['name'],
'email' => $request['email'],
'password' => Hash::make($request['password']),
]);
Auth::guard('admin')->login($admin);
return redirect('admin/dashboard');
}
}
bladeも追加。
views\admin\register.blade.php
<x-guest-layout>
<x-jet-authentication-card>
<x-slot name="logo">
<x-jet-authentication-card-logo />
</x-slot>
<x-jet-validation-errors class="mb-4" />
<form method="POST" action="{{ route('admin.register') }}">
@csrf
<div>
<x-jet-label for="name" value="{{ __('Name') }}" />
<x-jet-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
</div>
<div class="mt-4">
<x-jet-label for="email" value="{{ __('Email') }}" />
<x-jet-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required />
</div>
<div class="mt-4">
<x-jet-label for="password" value="{{ __('Password') }}" />
<x-jet-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" />
</div>
<div class="mt-4">
<x-jet-label for="password_confirmation" value="{{ __('Confirm Password') }}" />
<x-jet-input id="password_confirmation" class="block mt-1 w-full" type="password" name="password_confirmation" required autocomplete="new-password" />
</div>
<div class="flex items-center justify-end mt-4">
<a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('admin.login') }}">
{{ __('Already registered?') }}
</a>
<x-jet-button class="ml-4">
{{ __('Register') }}
</x-jet-button>
</div>
</form>
</x-jet-authentication-card>
</x-guest-layout>
ルーティング追加
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AdminDashboardController;
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\Auth\RegisteredUserController;
/*
|--------------------------------------------------------------------------
| 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');
});
Route::get('/welcome', function () {
return view('welcome');
});
Route::get('admin/', function () {
return view('admin/welcome');
});
Route::get('admin/welcome', function () {
return view('admin/welcome');
});
Route::prefix('admin')->group(function () {
/*Route::get('/', function () {
if(Route::middleware('auth:admin')){
return redirect()->route('admin.dashboard');
}else{
return redirect()->route('admin.login');
}
});*/
Route::get('login', [LoginController::class, 'create'])->name('admin.login');
Route::post('login', [LoginController::class, 'store']);
Route::middleware('auth:admin')->group(function () {
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('admin.dashboard');
});
Route::post('register', [RegisteredUserController::class, 'store']);
Route::get('register', [RegisteredUserController::class, 'create'])->name('admin.register');
});
Route::middleware(['auth:web', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified'
])->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
});
welcome画面を利用するように修正してみた。