マルチログイン機能というのは複数のユーザー区分があって、それぞれモデルとテーブルがあって、それぞれ独立してログインできるような機能を指しています。(ユーザーと管理者など)
Laravelでマルチログイン機能を実現するには今のところ、通常の認証機能をコピーして作成するしかないようです。(現在2022年8月)
BreezeやJetstreamなどLaravelにはログイン認証用のライブラリがあるようですが、laravel/uiを使用しようと思います。
※Laravelのインストールなどはお済の前提で進めさせていただきます。
概要
以下の手順で進めていきたいと思います。
⓪laravel/uiを導入(未導入の場合)
①ログインユーザーを追加(config/auth.php)
②ログイン後のリダイレクト先を設定(app/Http/Middleware/RedirectIfAuthenticated.php)
③ログインしてない時のリダイレクト先を設定(app/Exceptions/Handler.php)
④テーブルの作成(migration)
⑤モデルの作成(Illuminate/Foundation/Auth/Userをextendsする必要がある)
⑥コントローラ・ビューの作成(登録・ログイン・ホーム。コピーして作る)
⑦ルーティング
⑧特別編:パスワード再設定
今回新しく作るユーザーはadminとします。
⓪laravel/uiを導入(未導入の場合)
すでに導入済みの場合は飛ばしてください。
composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev
2行目の箇所は、reactやvueを使っている場合は下記のようにするといいようです。
php artisan ui react --auth #reactを使っている場合
php artisan ui vue --authn #vueを使っている場合
php artisan ui ○○をすると、コントローラーとviewが作成されます。
それぞれauthディレクトリの中に作成されます。
これはデフォルトのuser用のものなので、後々これをコピーして第2ユーザ(admin)用の機能を機能を作成していきます。
/registerにアクセスすると、user用の登録画面が開かれると思います。
①ログインユーザーを追加(config/auth.php)
下記3つを設定します。すべてconfig/auth.phpに追記します。
・認証管理の方法(セッションかトークンか)
・モデルとログイン方法(eloquentかdatabase)の指定
・パスワード再設定で使用するテーブルなどの指定
認証管理の方法(セッションかトークンか)
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'user' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [ //追記
'driver' => 'session', //追記
'provider' => 'admins', //追記
], //追記
],
モデルとログイン方法(eloquentかdatabase)の指定
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [ //追記
'driver' => 'eloquent', //追記
'model' => App\Models\Admin::class, //追記
], //追記
],
パスワード再設定で使用するテーブルなどの指定
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
'admins' => [ //追記
'provider' => 'admins', //追記
'table' => 'password_resets', //追記
'expire' => 60, //追記
'throttle' => 60, //追記
], //追記
],
②認証済みの場合のリダイレクト先(app/Http/Middleware/RedirectIfAuthenticated.php)
認証済みの時にログインページにアクセスするとすべてhomeに飛ばされるようになっています。
adminで認証済みの状態で、admin/loginにアクセスした時はadmin/homeに飛ばしたいので、その設定を行います。
ファイルはapp/Http/Middleware/RedirectIfAuthenticated.phpです。
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if($guard == "admin" && Auth::guard($guard)->check()) { //追記
return redirect('admin/home'); //追記
} //追記
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
③認証してない時のリダイレクト先を設定(app/Exceptions/Handler.php)
上記とは逆で、認証してない状態で認証が必要なページ(homeなど)にアクセスするとログインページに飛ばされるようになっています。
認証してない状態でadmin/homeにアクセスした時はadmin/loginに飛ばしたいので、その設定を行います。
ファイルはapp/Exceptions/Handler.phpです。
protected function unauthenticated($request,Throwable $exception)
{
if($request->expectsJson()) {
return respomse()->json(['message' => $exception->getMessage()],401);
}
if($request->is('admin') || $request->is('admin/*')){ //追記
return redirect()->guest('/admin/login'); //追記
} //追記
return redirect()->guest($exception->redirectTo ?? route('login'));
}
④テーブルの作成(migration)
通常通りです。カラムは、名前・メールアドレス・パスワードと最低限にしています。
php artisan make:migration create_admins_table
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();
});
}
php artisan migrate
⑤モデルの作成(Illuminate/Foundation/Auth/Userをextendsする必要がある)
Adminモデルを作成して下記のように修正します。
普通のモデルとの違いはIlluminate/Foundation/Auth/Userを継承するところです。
php artisan make:model Admin
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User;
use Illuminate\Notifications\Notifiable;
class Admin extends User
{
use HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
}
⑥コントローラ・ビューの作成(登録・ログイン・ホーム。コピーして作る)
ここまで作れば、あとは自動で生成されているファイルをコピーしながらコントローラ・ビューを作成していくだけです。
登録・ログイン・ホームを作成していきます。
登録
app/Http/Controllers/Auth/RegisterController.phpをコピーして、app/Http/Controllers/admin/RegisterController.phpとして作成して、下記のように修正します。
namespace App\Http\Controllers\admin; //修正
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\Admin; //修正
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; //追記
class RegisterController extends Controller
{
use RegistersUsers;
protected $redirectTo = '/admin/home'; //修正
public function __construct()
{
$this->middleware('guest:admin'); //修正
}
protected function guard() //追記
{ //追記
return Auth::guard('admin'); //追記
} //追記
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:admins'], //修正
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
protected function create(array $data)
{
return Admin::create([ //修正
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}
resources/views/auth/register.blade.phpをコピーしてresources/views/admin/register.blade.phpを作成します。
Formの送信先を変更します。
<form method="POST" action="{{ url('admin/register') }}"> //修正
@csrf
ログイン&ログアウト
app/Http/Controllers/Auth/LoginController.phpをコピーしてapp/Http/Controllers/admin/LoginController.phpを作成して、下記のように修正します。
namespace App\Http\Controllers\admin; //修正
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth; //追記
class LoginController extends Controller
{
// use AuthenticatesUsers; //削除
use AuthenticatesUsers { //追記
logout as performLogout; //追記
} //追記
protected $redirectTo = '/admin/home'; //修正
public function __construct()
{
$this->middleware('guest:admin')->except('logout'); //修正
}
protected function guard() //追記
{ //追記
return Auth::guard('admin'); //追記
} //追記
public function logout(Request $request) //追記
{ //追記
$this->performLogout($request); //追記
return redirect('admin/login'); //追記
} //追記
}
resources/views/auth/login.blade.phpをコピーして
resources/views/admin/login.blade.phpを作成します。
Formの送信先を変更します。
パスワード再設定の遷移先も変更します。
<form method="POST" action="{{ url('admin/login') }}">
@csrf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@if (Route::has('password.request')) //削除
<a class="btn btn-link" href="{{ url('admin/password/request') }}"> //修正
パスワードを忘れた方はこちら
</a>
@endif //削除
ホーム
resources/views/home.blade.phpをコピーしてresources/views/admin/home.blade.phpを作成します。
読み込むレイアウトを変更します。
@extends('layouts.admin') //修正
resources/views/layouts/app.blade.phpをコピーして、resources/views/layouts/admin.blade.phpを作成します。
登録ボタン・ログインボタン・ログアウトボタンの遷移先を変更します。
@guest
<li class="nav-item">
<a class="nav-link" href="{{ url('admin/login') }}">{{ __('Login') }}</a> //修正
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url('admin/register') }}">{{ __('Register') }}</a> //修正
</li>
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-end" 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="{{ url('admin/logout') }}" method="POST" class="d-none"> //修正
@csrf
</form>
</div>
</li>
@endguest
⑦ルーティング
routes/web.phpに下記を追記します。
Route::view('/admin/login', 'admin/login');
Route::post('/admin/login', [App\Http\Controllers\admin\LoginController::class, 'login']);
Route::post('admin/logout', [App\Http\Controllers\admin\LoginController::class,'logout']);
Route::view('/admin/register', 'admin/register');
Route::post('/admin/register', [App\Http\Controllers\admin\RegisterController::class, 'register']);
Route::view('/admin/home', 'admin/home')->middleware('auth:admin');
これで第2ユーザー(ここではadmin)の登録・ログイン・ホームへのアクセスができるようになったと思います。
⑧特別編:パスワード再設定
パスワード再設定は、上記の登録やログインとはやり方が異なるので、別で解説させていただきます。
/password/resetにアクセスすると、パスワード再設定が行えると思います。
これをコピーして第2ユーザー用にパスワード再設定機能を作成します。
概要
⓪メール設定
①コントローラ(ForgotPasswordControllerとResetPasswordController)
②ビュー(passwords/email.blade.phpとpasswords/reset.blade.php)
③パスワードリセットのリンクURLを設定(モデルとNotification)
④ルーティング
⓪メール設定
すでに設定済みの場合は飛ばしてください。
.envファイルで設定します。下記ではテストツールのmailhogを利用してますが、通常のメールサーバーも指定できます。
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=user
MAIL_PASSWORD=password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
①コントローラ(ForgotPasswordControllerとResetPasswordController)
app/Http/Controllers/Auth/ForgotPasswordController.phpをコピーして、app/Http/Controllers/admin/ForgotPasswordController.phpを作成して修正します。
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password; //追記
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
protected function broker() //追記
{ //追記
return Password::broker('admins'); //追記
} //追記
}
app/Http/Controllers/Auth/ResetPasswordController.phpをコピーして、app/Http/Controllers/admin/ResetPasswordController.phpを作成して修正します。
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Support\Facades\Password; //追記
use Illuminate\Http\Request; //追記
class ResetPasswordController extends Controller
{
use ResetsPasswords;
protected $redirectTo = 'admin/home'; //修正
protected function broker() //追記
{ //追記
return Password::broker('admins'); //追記
} //追記
}
②ビュー(passwords/email.blade.phpとpasswords/reset.blade.php)
resources/views/auth/passwords/email.blade.phpをコピーして、
resources/views/admin/passwords/email.blade.phpを作成して遷移先を修正します。
<form method="POST" action="{{ url('admin/password/email') }}"> //修正
@csrf
resources/views/auth/passwords/reset.blade.phpをコピーして、
resources/views/admin/passwords/reset.blade.phpを作成して遷移先を修正します。
<form method="POST" action="{{ url('admin/password/reset') }}"> //修正
@csrf
③パスワードリセットのリンクURLを設定(モデルとNotification)
app/Models/Admin.phpに下記を追記します。
use App\Notifications\ResetPasswordNotification; //追記
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public function sendPasswordResetNotification($token) //追記
{ //追記
$url = url("admin/password/reset/$token"); //追記
$this->notify(new ResetPasswordNotification($url)); //追記
} //追記
コマンドでNotificationを作成して修正します。
php artisan make:notification ResetPasswordNotification
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class ResetPasswordNotification extends Notification
{
use Queueable;
public function __construct($url) //修正
{
$this->url = $url; //修正
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', "$this->url?email=$notifiable->email") //修正
->line('Thank you for using our application!');
}
}
④ルーティング
Route::view('/admin/password/reset', 'admin/passwords/email');
Route::post('/admin/password/email', [App\Http\Controllers\admin\ForgotPasswordController::class, 'sendResetLinkEmail']);
Route::view('/admin/password/reset/{token}', [App\Http\Controllers\admin\ResetPasswordController::class,'showResetForm']);
Route::post('/admin/password/reset', [App\Http\Controllers\admin\ResetPasswordController::class, 'reset']);
以上になります!
コメント