Найти тему
Максим Гречушников

9 советов по авторизации в Laravel

Оглавление

У Laravel есть отличная готовая система Auth, но, конечно, нам нужно настроить вещи здесь и там. Для некоторых из них не нужно искать внешние пакеты или писать много пользовательского кода, давайте исследуем, какие интересные способности скрываются под капотом Auth.

Совет 1. Параметры Auth:: routes()

Мы все, вероятно, знаем метод Auth::routes(), который исходит из пакета пользовательского интерфейса Laravel (до Laravel 7 он был включен в ядро).

Но знаете ли вы, что он может принимать массив параметров для включения/отключения определенных маршрутов Auth?

Начиная с Laravel 7, вот возможные параметры с их значениями по умолчанию:

Auth::routes([
'login' => true,
'logout' => true,
'register' => true,
'reset' => true, // for resetting passwords
'confirm' => false, // for additional password confirmations
'verify' => false, // for email verification
]);

Эти параметры просто включают или отключают некоторые маршруты.

Чтобы понять, как они работают, вы можете посмотреть на файл AuthRouteMethods в Laravel UI:

return function ($options = []) {
// Login Routes...
if ($options['login'] ?? true) {
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
}

// Logout Routes...
if ($options['logout'] ?? true) {
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
}

// Registration Routes...
if ($options['register'] ?? true) {
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register');
}

// Password Reset Routes...
if ($options['reset'] ?? true) {
$this->resetPassword();
}

// Password Confirmation Routes...
if ($options['confirm'] ??
class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) {
$this->confirmPassword();
}

// Email Verification Routes...
if ($options['verify'] ?? false) {
$this->emailVerification();
}
};

Совет 2. Laravel UI: генерация только контроллеров

Официальная документация определяет этот основной способ использования пользовательского интерфейса Laravel:

php artisan ui vue --auth

Но что делать, если вам не нужен визуальный интерфейс? Что делать, если вы создаете только проект на основе API, и у вас нет никакого интерфейса на стороне Laravel?

Вы все еще можете использовать Laravel Auth и его контроллеры. Установите пакет пользовательского интерфейса Laravel и запустите его:

php artisan ui:controllers

Он будет генерировать только app/Http/Controllers/Auth содержимое, поэтому вам не нужны файлы Blade/Vue для их использования.

Смотрите реализацию этой команды Artisan в репозитории Github.

Совет 3. Повторное подтверждение пароля для важных настроек

Вы когда-нибудь поддерживали репозиторий Github и пытались изменить его настройки доступа? Затем Github просит вас снова ввести свой пароль, просто чтобы убедиться, что это вы.

Начиная с Laravel 6.2, у нас также есть эта функция в фреймворке.

-2

Все, что вам нужно сделать, это добавить middleware password.confirm к маршруту(маршрутам), который вы хотите защитить.

Route::get('/secrets', 'SecretsController@show')->middleware('password.confirm');

Цитирую Дриса винца из официальной статьи о выпуске полнометражных игр:

Если вы попытаетесь получить доступ к маршруту, вам будет предложено подтвердить свой пароль, подобный тому, что вы, возможно, видели в других приложениях, таких как GitHub.Подтверждение пароля сохранит метку времени в сеансе пользователя, который по умолчанию длится три часа, так что пользователям не придется снова вводить свой пароль в течение этого периода.Вы можете настроить эту продолжительность, используя новый password_timeout параметр конфигурации в файле config/auth.php

Совет 4. Выполнить выход со всех устройств

Начиная с Laravel 5.6, у нас есть отдельный метод автоматического выхода из системы любых других устройств или браузеров, которые вошли в систему с нашей учетной записью:

Auth::logoutOtherDevices($password);

Обычно это делается для выхода из системы других устройств, когда текущее устройство успешно вошло в систему. Чтобы сделать это, мы переопределяем метод authenticated()из призфайла нака AuthenticatesUsers.php и помещаем его в app/Http/Controllers/Auth/LoginController.php:

protected function authenticated(Request $request, $user)
{
\Auth::logoutOtherDevices(request('password'));
}

Кроме того, не забудьте активировать middleware  AuthenticateSession в app/Http/Kernel.php файле, которое по умолчанию закомментировано:

protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

Перенаправление после входа в систему/ регистрации: пользовательская логика

По умолчанию и Laravel LoginController, и RegisterController имеют одно и то же свойство:

class RegisterController extends Controller
{
protected $redirectTo = RouteServiceProvider::HOME;

Таким образом, вы можете указать, на какой URL нужно перенаправить после успешного входа в систему/регистрации. Значение по умолчанию находится вapp/Providers/RouteServiceProvider.php:

class RouteServiceProvider extends ServiceProvider
{
public const HOME = '/home';

Как вы можете настроить его?

Во-первых, вы можете изменить значение этого $redirectTo свойства на какую-то другую константу и, возможно, отдельно для входа и регистрации.

Но что делать, если у вас есть более сложная логика динамического редиректа, которая зависит, например, от роли пользователя?

Вы можете создать метод в контроллере Auth, вызвать его redirectTo()и указать свои условия внутри. Этот метод будет переопределять любые значения $redirectTo свойств.

См. пример:

class RegisterController extends Controller
{
protected $redirectTo = RouteServiceProvider::HOME;

protected function redirectTo()
{
if (auth()->user()->role_id == 1) {
return '/admin';
}
return '/home';
}

Совет 5. Быстрое Создание Новых Пользователей

Что делать, если вам нужно создать одного нового пользователя, а у вас нет готовой регистрационной формы?

Просто откройте Laravel Tinker в своем терминале:

php artisan tinker

Если вы не знакомы с Tinker, это инструмент командной строки для выполнения любого кода Laravel/PHP. Таким образом, вы можете легко создать пользователя, набрав эту команду и нажав Enter:

\App\User::create(['name' => 'Admin', 'email' => 'admin@admin.com', 'password' => bcrypt('somesecurepassword')]);

Но что делать, если вам нужно создать много пользователей для тестирования, например, 10, или 100, или 1000? Нет проблем, мы можем использовать factories класс, который поставляется по умолчанию с Laravel, вdatabase/factories/UserFactory.php:

$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
});

Это значения по умолчанию для «поддельного» пользователя, которого мы можем создать. Для этого мы создадим файл сидера:

php artisan make:seeder UsersSeeder

Затем мы открываем этот сгенерированный файл database/seeds/UsersSeeder.php и заполняем run() метод следующим образом:

public function run()
{
// This will create 100 users
factory(App\User::class, 100)->create();
}

Чтобы запустить его, нам нужно выполнить эту команду:

php artisan db:seed --class=UsersSeeder

Вы можете прочитать больше о заполнении базы данных в официальной документации Laravel.

Совет 6. Войдите в систему с помощью электронной почты или имени пользователя

По умолчанию пользователи Laravel аутентифицируются с помощью электронной почты и пароля. Но что делать, если Ваш идентификатор не является электронной почтой? Какое-нибудь имя пользователя, например.

Вы можете легко изменить его, переопределив один метод из AuthenticatesUsers.php.

Вот значение по умолчанию:

trait AuthenticatesUsers
{
// ... other methods

public function username()
{
return 'email';
}

Вы можете скопировать это в свой LoginController.php и просто изменить значение:

class LoginController extends Controller
{
use AuthenticatesUsers;

// ... other methods

public function username()
{
return 'username';
}
}

Давайте сделаем еще один шаг вперед. Что делать, если ваши пользователи могут войти в систему с помощью электронной почты или имени пользователя? Таким образом, есть поле ввода под названием “Электронная почта/имя пользователя”, и они могут ввести одно или другое.

Давайте добавим «трюк» к тому же username()методу сверху. Мы проверяем, является ли введенная строка электронной почтой, в противном случае мы рассматриваем ее как имя пользователя. Эта проверка-функция PHP, даже не Laravel.

class LoginController extends Controller
{
// ...

public function username()
{
return filter_var(request('email'), FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
}
}

Обратите внимание : не забывайте, что в вашей форме входа в систему вам нужно перейти input type="email"на type="text"

Совет 7. Слишком много попыток входа в систему: настройка параметров

Если вы попытаетесь войти в систему с недействительными учетными данными более пяти раз в течение одной и той же минуты, вы будете заблокированы с сообщением слишком много попыток входа в систему. Пожалуйста, повторите попытку через X секунд.

Этот блок будет активен в течение 1 минуты, и он уникален для имени пользователя/электронной почты и их IP-адреса.

Вы можете настроить эти параметры:

  • Количество допустимых попыток в течение минуты (по умолчанию пять попыток)
  • Сколько минут нужно блокировать входы в систему (по умолчанию 1 минута)

Эти два параметра находятся внутри трейта ThrottlesLogins:

trait ThrottlesLogins
{
// ... other methods

/**
* Get the maximum number of attempts to allow.
*
* @return int
*/
public function maxAttempts()
{
return property_exists($this, 'maxAttempts') ? $this->maxAttempts : 5;
}

/**
* Get the number of minutes to throttle for.
*
* @return int
*/
public function decayMinutes()
{
return property_exists($this, 'decayMinutes') ? $this->decayMinutes : 1;
}
}

Таким образом, чтобы переопределить их, вы можете указать свойства внутри вашего LoginController:

class LoginController extends Controller
{
protected $maxAttempts = 3; // Default is 5
protected $decayMinutes = 2; // Default is 1

// ...
}

Совет 8. Регистрация: отключить автоматический вход в систему

По умолчанию вновь зарегистрированный пользователь автоматически входит в систему и перенаправляется на домашнюю страницу.

Если вы хотите отключить это и вместо этого показать какую-то страницу “успех”, не создавая автоматически сеанс пользователя, вот что вы можете сделать.

Оригинальный метод регистрации находится внутри трейта RegistersUsers:

trait RegistersUsers
{
public function register(Request $request)
{
$this->validator($request->all())->validate();

event(new Registered($user = $this->create($request->all())));

$this->guard()->login($user);

if ($response = $this->registered($request, $user)) {
return $response;
}

return $request->wantsJson()
? new Response('', 201)
: redirect($this->redirectPath());
}

Таким образом, ваша цель состоит в том, чтобы переопределить его RegisterController и вернуть перенаправление на новую страницу, а не войти в систему:

class RegisterController extends Controller
{
use RegistersUsers;

public function register(Request $request)
{
$this->validator($request->all())->validate();

event(new Registered($user = $this->create($request->all())));

return redirect()->route('your_success_page_route_name');
}

Совет 9. Логин: дополнительная проверка с помощью электронной почты / пароля

Что делать, если вам нужна дополнительная проверка, в дополнение к электронной почте и паролю по умолчанию? Например, вы хотите проверить, активен ли пользователь или нет.

Вы можете добавить дополнительные элементы в credentials массив, который определен в трейте AuthenticatesUsers:

trait AuthenticatesUsers
{
// ...

protected function credentials(Request $request)
{
return $request->only($this->username(), 'password');
}

Вы просто переопределяете это LoginController и добавляете все, что хотите:

class LoginController extends Controller
{
// ...

protected function credentials(Request $request)
{
return $request->only($this->username(), 'password') + ['is_active' => 1];
}

Обратите внимание: это интересный Быстрый совет, но я бы посоветовал вам выполнить такую дополнительную проверку в отдельном middleware, тогда вы могли бы предоставить пользователю более явное сообщение об ошибке, а не ошибку учетных данных по умолчанию.

Вот и все, это быстрые советы, но есть гораздо больше возможностей для расширения с помощью пользовательского кода и внешних пакетов. Так что следите за обновлениями, чтобы получить больше статей на эту тему!