Слава Україні!

Laravel 9. Аутентификация из разных таблиц

Аутентификация на сайте из разных таблиц в Laravel 9
2-го січня 2023, 13:45

Иногда возникает необходимость организовать аутентификацию на сайте из разных таблиц. Например, при создании магазина, меня не устраивает то, что клиенты и менеджеры (администраторы) лежат вперемежку в одной таблице. Да и вход в админку (которая лежит на поддомене) лучше "замкнуть" на отдельную таблицу в которой хранятся учетные записи сугубо администраторов.

Создаем модель Admin (сразу с миграцией). По сути они представляют собой копию модели и миграциии User. php artisan make:model Admin -m:


use IlluminateSupportFacadesSchema;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;

class CreateAdminsTable extends Migration
{
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('admins');
    }
}

Запускаем: php artisan migrate

В самом минимальном виде модель админа будет работать в таком виде:


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;

    protected $guard = "admin";

    protected $fillable = ['name', 'email', 'password'];

    protected $hidden = ['password', 'remember_token'];
}
Обратите внимание на то, что модель Admin не является наследником Illuminate\Database\Eloquent\Model как это присуще обычным моделям, а расширяет Illuminate\Foundation\Auth\User. Это важный момент.

Настраиваем guard в файле config/auth.php.


'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],

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

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => AppUser::class,
    ],
    'admins' => [
        'driver' => 'eloquent',
        'model' => AppAdminAdmin::class,
    ]
],

'passwords' => [
    'users' => [
        'provider' => 'users',
        'table' => 'password_resets',
        'expire' => 60,
        'throttle' => 60,
    ],
    'admins' => [
        'provider' => 'admins',
        'table' => 'password_resets',
        'expire' => 60,
        'throttle' => 60,
    ],
],

Далее, в контроллер аутентификация LoginController, добавляем методы для входа администратора:


namespace App\Http\Controllers\Auth;

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

class LoginController extends Controller
{
    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
        $this->middleware('guest:admin')->except('logout');
    }

    public function showAdminLoginForm()
    {
        return view('auth.login', ['route' => route('admin.login'), 'title'=>'Admin']);
    }

    public function adminLogin(Request $request)
    {
        $this->validate($request, [
            'email'   => 'required|email',
            'password' => 'required|min:6'
        ]);

        if ($this->guard()
            ->attempt($request->only(['email','password']), $request->get('remember'))
        ) {
            return redirect()->intended('/admin/dashboard');
        }

        return back()->withInput($request->only('email', 'remember'));
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard('admin');
    }
}
        

Немного изменим представление 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">{{ $title ?? "" }} {{ __('Login') }}</div>

                <div class="card-body">
                    @isset($route)
                        <form method="POST" action="{{ $route }}">
                    @else
                        <form method="POST" action="{{ route('login') }}">
                    @endisset

                    @csrf

            ...

Далее необходимо изменить метод перенаправления после успешного входа в систему. Это делается в middleware RedirectIfAuthenticated


namespace App\Http\Middleware;

use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
    /**
     * 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;

        foreach ($guards as $guard) {
            if ($guard == "admin" && Auth::guard($guard)->check()) {
                return redirect('/admin/dashboard');
            }

            if (Auth::guard($guard)->check()) {
                return redirect(RouteServiceProvider::HOME);
            }
        }

        return $next($request);
    }
}

Если требуется регистрация для админов - добавляем методы в RegisterController:


namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
        $this->middleware('guest:admin');
    }

    // ... Other methods

    public function showAdminRegisterForm()
    {
        return view('auth.register', ['route' => route('admin.register'), 'title'=>'Admin']);
    }

    protected function createAdmin(Request $request)
    {
        $this->validator($request->all())->validate();
        $admin = Admin::create([
            'name' => $request['name'],
            'email' => $request['email'],
            'password' => Hash::make($request['password']),
        ]);
        return redirect()->intended('admin');
    }
}

И соответственно изменяем register.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">{{ $title ?? "" }} {{ __('Register') }}</div>

                <div class="card-body">
                    @isset($route)
                        <form method="POST" action="{{ $route }}">
                    @else
                        <form method="POST" action="{{ route('register') }}">
                    @endisset

                    @csrf

            ...

Осталось только прописать маршруты в файле routes/web.php.


Auth::routes();

Route::get('/admin', 'Auth\LoginAdminController@showAdminLoginForm')->name('admin.login-form');
Route::post('/admin', 'Auth\LoginAdminController@adminLogin')->name('admin.login');

Route::get('/admin/register', 'Auth\RegisterController@showAdminRegisterForm')->name('admin.register-form');
Route::post('/admin/register', 'Auth\RegisterController@createAdmin')->name('admin.register');

Route::get('/home', [HomeController::class, 'index'])->name('home');
Route::get('/admin/dashboard',function(){
    return view('admin');
})->middleware('auth:admin');
Проверить пользователей теперь можно так:

@if(auth()->guard('admin')->check())
    Hello {{auth()->guard('admin')->user()->name}}
@elseif(auth()->guard('web')->check())
    Hello {{auth()->guard('web')->user()->name}}
@endif

Важлива інформація

Міністерство оборони
України
з 24.02 по 20.04
втрати противника
орієнтовно склали:
941100 ( +950 ) особового складу
10677 ( +1 ) танків
22271 ( +5 ) бойових бронемашин
26649 ( +49 ) артилерійських систем
1368 ( +0 ) РСЗВ
1139 ( +0 ) засоби ППО
370 ( +0 ) літаків
335 ( +0 ) гелікоптерів
45274 ( +112 ) автомобільної техніки
28 ( +0 ) кораблі / катери
3148 ( +0 ) крилаті ракети
33240 ( +64 ) БПЛА
3859 ( +1 ) спец. техніки
4 ( +0 ) установок ОТРК/ТРК
1 ( +0 ) підводні човни