Laravel マルチログイン設定
マルチログイン流れ
1.モデル&マイグレーション設定
-m
オプションでマイグレーションとモデルを同時に作成できる。
app/models
配下にモデルファイルが生成される
既存のuser.php
の内容(use文とクラス内容)をowner,adminそれぞれにコピーする
マイグレーションファイルもusers_tableのupメソッドの中身の$tableをowner,adminそれぞれにコピーする
全てできたらphp artisan migrate
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\User as Authenticatable; class Admin extends Authenticatable { use HasFactory; /** * The attributes that are mass assignable. * * @var array<int, string> */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for serialization. * * @var array<int, string> */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast. * * @var array<string, string> */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }
adminのマイグレーションファイル
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAdminsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('admins', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admins'); } }
次にパスワードリセットテーブルを作成する。
owner_passwarod_resets
とadmin_password_resets
テーブル作成
$ php artisan make:migration create_テーブル名
最初からあるuserのpassword_resets_tableのup
メソッド内をコピーしてadmin,ownerそれぞれのマイグレーションファイルへ貼り付ける
貼り付けたらphp artisan migrate
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAdminPasswordResets extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('admin_password_resets', function (Blueprint $table) { $table->string('email')->index(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admin_password_resets'); } }
2. ルート設定
bleezeをいれるとweb.php
,auth.php
が追加されている
Userで使ってるのはweb.php
とauth.php
Owner用の routes/owner.php
, Admin用の routes/admin.php
をそれぞれエディタから作成する
→web.php
の中身を全部コピーして貼り付ける
require __DIR__.'/auth.php’;
をけしてauth.php
の中身をすべてはりつける
use文を上にもっていき、controllers配下にそれぞれuser,admin,ownerでフォルダを作成するので書き加える
admin.php
use App\Http\Controllers\Admin\Auth\AuthenticatedSessionController;
auth.php
use App\Http\Controllers\User\Auth\AuthenticatedSessionController;
owner.php
use App\Http\Controllers\Owner\Auth\AuthenticatedSessionController;
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\Admin\Auth\AuthenticatedSessionController; use App\Http\Controllers\Admin\Auth\ConfirmablePasswordController; use App\Http\Controllers\Admin\Auth\EmailVerificationNotificationController; use App\Http\Controllers\Admin\Auth\EmailVerificationPromptController; use App\Http\Controllers\Admin\Auth\NewPasswordController; use App\Http\Controllers\Admin\Auth\PasswordResetLinkController; use App\Http\Controllers\Admin\Auth\RegisteredUserController; use App\Http\Controllers\Admin\Auth\VerifyEmailController; Route::get('/', function () { return view('welcome'); }); Route::get('/dashboard', function () { return view('dashboard'); })->middleware(['auth'])->name('dashboard'); Route::middleware('guest')->group(function () { Route::get('register', [RegisteredUserController::class, 'create']) ->name('register'); Route::post('register', [RegisteredUserController::class, 'store']); Route::get('login', [AuthenticatedSessionController::class, 'create']) ->name('login'); Route::post('login', [AuthenticatedSessionController::class, 'store']); Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) ->name('password.request'); Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) ->name('password.email'); Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) ->name('password.reset'); Route::post('reset-password', [NewPasswordController::class, 'store']) ->name('password.update'); }); Route::middleware('auth')->group(function () { Route::get('verify-email', [EmailVerificationPromptController::class, '__invoke']) ->name('verification.notice'); Route::get('verify-email/{id}/{hash}', [VerifyEmailController::class, '__invoke']) ->middleware(['signed', 'throttle:6,1']) ->name('verification.verify'); Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) ->middleware('throttle:6,1') ->name('verification.send'); Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) ->name('password.confirm'); Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) ->name('logout'); });
3.ルートサービスプロバイダ設定
画面を読み込むたびに実行される内容
定数としてOwner, AdminそれぞれホームURLを設定
public const OWNER_HOME = ‘/owner/dashboard’
public const ADMIN_HOME = ‘/admin/dashboard’
Userがログインしたらdashboradにリダイレクトがかかる
Owner,Adminそれぞれログインしたらリダイレクト先の設定
Bootメソッドにuser, owner, adminのURLを追記 Prefixをつける, as()で名前付きルートにもprefixつける
<?php namespace App\Providers; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Route; class RouteServiceProvider extends ServiceProvider { /** * The path to the "home" route for your application. * * This is used by Laravel authentication to redirect users after login. * User,Admin,Ownerそれぞれのログイン後のリダイレクト先パスの設定 * @var string */ public const HOME = '/dashboard'; public const OWNER_HOME = '/owner/dashboard'; public const ADMIN_HOME = '/admin/dashboard'; /** * The controller namespace for the application. * * When present, controller route declarations will automatically be prefixed with this namespace. * * @var string|null */ // protected $namespace = 'App\\Http\\Controllers'; /** * Define your route model bindings, pattern filters, etc. * ルート情報を設定する。bootメソッド→サービスプロバロバイダが読み込まれたあとに実行されるメソッド * ルートパターンはミドルウェアのap1とwebを使う2パターン * laravelでviewを表示してリクエストレスポンスを返すパターンは→web * フロントをすべてjsなどで作る場合はapiを使う * @return void */ public function boot() { $this->configureRateLimiting(); $this->routes(function () { //prefix→URLの頭にapiとつける。Routeはファサード内のメソッドを使ってる Route::prefix('api') //ミドルウェアをグループのすべてのルートに割り当てる ->middleware('api') ->namespace($this->namespace) //ヘルパ関数base_pathでroutes/api.phpのすべてのURLにapiがつく ->group(base_path('routes/api.php')); //prefix('/')でownerやadminがついてないURLは/へ遷移させる Route::prefix('/') //asをつけることで別名にできる'/'→'user.'に変わる ->as('user.') ->middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); //owner用 Route::prefix('owner') ->as('owner.') ->middleware('web') ->namespace($this->namespace) ->group(base_path('routes/owner.php')); //admin用 Route::prefix('admin') ->as('admin.') ->middleware('web') ->namespace($this->namespace) ->group(base_path('routes/admin.php')); }); } /** * Configure the rate limiters for the application. * * @return void */ protected function configureRateLimiting() { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); } }
4. ガード設定
Laravel標準の認証機能→ user owner adminに認証機能をつける
今回は以下3つを指定
guards
・・今回はsession
Providers
・・今回はEloquent(モデル)
passwordReset
・・生成したテーブル名 をそれぞれ設定
<?php return [ 'defaults' => [ 'guard' => 'users', 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'users' => [ 'driver' => 'session', 'provider' => 'users', ], 'owners' => [ 'driver' => 'session', 'provider' => 'owners', ], 'admin' => [ 'driver' => 'session', 'provider' => 'admin', ], ], //それぞれのモデルから情報を取得する 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], 'owners' => [ 'driver' => 'eloquent', 'model' => App\Models\Owner::class, ], 'admin' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], /* |expire 期限(日)、throttle ログイン失敗したら制限(秒) |provider→1つ上で設定したやつ |table→マイグレーションファイルで設定した名前 */ 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], 'owners' => [ 'provider' => 'owners', 'table' => 'owner_password_resets', 'expire' => 60, 'throttle' => 60, ], 'admin' => [ 'provider' => 'admin', 'table' => 'admin_password_resets', 'expire' => 60, 'throttle' => 60, ],
5. Middreware設定
Middleware/Authenticate.php
ユーザが未認証の場合のリダイレクト処理
たとえばownerのURLにownerでログインしてなかったらownerのログイン画面に飛ばす
Middleware/RedirectIfAuthenticated
ログイン済みユーザーがアクセスしてきたら リダイレクト処理
ownerがログインした状態でowner画面を表示した場合
<?php namespace App\Http\Middleware; use Illuminate\Support\Facades\Route; use Illuminate\Auth\Middleware\Authenticate as Middleware; class Authenticate extends Middleware { //RouteServiceProviderでuser.などは定義済 protected $user_route = 'user.login'; protected $owner_route = 'owner.login'; protected $admin_route = 'admin.login'; /** * 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) { //もしリクエストはjsonじゃなかったら if (! $request->expectsJson()) { //もしowner関連のURLじゃなかったら$owner_routeへ飛ばす if(Route::is('owner.*')){ //$this->で上のプロパティ呼び出し return route($this->owner_route); //admin関連のURLでなかったらadminへ飛ばす } elseif(Route::id('admin*')){ return route($this->admin_route); //ownerでもadminでもなかったらユーザーへ飛ばす } else { return route($this->user_route); } } } }
<?php namespace App\Http\Middleware; use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { //config/appで定義 private const GUARD_USER = 'users'; private const GUARD_ADMIN = 'admin'; private const GUARD_OWNER = 'owners'; /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next * @param string|null ...$guards * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse */ public function handle(Request $request, Closure $next, ...$guards) { // $guards = empty($guards) ? [null] : $guards; // //Authのガードでチェックしてログインしてたらリダイレクト // foreach ($guards as $guard) { // if (Auth::guard($guard)->check()) { // return redirect(RouteServiceProvider::HOME); // } // } //もしuserとして認証してるか、またはuser関連のURLなら if(Auth::guard(self::GUARD_USER)->check()&& $request->routeIs('user.*')){ return redirect(RouteServiceProvider::HOME); } if(Auth::guard(self::GUARD_ADMIN)->check()&& $request->routeIs('admin.*')){ return redirect(RouteServiceProvider::ADMIN_HOME); } if(Auth::guard(self::GUARD_OWNER)->check()&& $request->routeIs('owner.*')){ return redirect(RouteServiceProvider::OWNER_HOME); } return $next($request); } }
6.リクエストクラス設定
ログインフォームに入力された値をDBの値と比較し、合っていたら認証する
User, Owner, Admin 3つのフォームがあるので routeIs() でルート確認しつつ Auth::guard() を追加
<?php /** * Attempt to authenticate the request's credentials. * * @return void * * @throws \Illuminate\Validation\ValidationException */ public function authenticate() { $this->ensureIsNotRateLimited(); //config/auth.phpのgurads設定と紐付ける //requestクラスなのでrouteIsがつかえる。ownerのログインフォームからきたらownersとする if($this->routeIs('owner*')){ $guard = 'owners'; //adminのログインフォームからきたらadminとする }elseif($this->routeIs('admin*')){ $guard = 'admin'; //それ以外ならusersとする }else{ $guard = 'users'; } //Auth::attemptで入力された情報からPWのemailを受け取る if (! Auth::guard($guard)->attempt($this->only('email', 'password'), $this->boolean('remember'))) { RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ 'email' => trans('auth.failed'), ]); } RateLimiter::clear($this->throttleKey()); }