Verification: a143cc29221c9be0

Php artisan make auth не работает laravel 8

Php artisan make auth не работает laravel 8

Введение

Многие веб-приложения предоставляют своим пользователям возможность аутентифицироваться в приложении и «войти в систему». Реализация этого функционала в веб-приложениях может быть сложной и потенциально рискованной задачей. По этой причине Laravel стремится предоставить вам инструменты, необходимые для быстрой, безопасной и простой реализации аутентификации.

По своей сути средства аутентификации Laravel состоят из «охранников» и «провайдеров». Охранники определяют, как пользователи проходят проверку подлинности для каждого запроса. Например, Laravel поставляется с охранником session, который поддерживает состояние, используя хранилище сессий и файлы Cookies.

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

Файл конфигурации аутентификации вашего приложения находится в config/auth.php. Этот файл содержит несколько хорошо задокументированных вариантов для настройки поведения служб аутентификации Laravel.

Охранников и провайдеров не следует путать с «ролями» и «разрешениями». Чтобы узнать больше об авторизации действий пользователя с помощью разрешений, обратитесь к документации по авторизации.

Стартовые комплекты

Хотите быстро начать работу? Установите стартовый комплект приложения в новое приложение Laravel. После миграции базы данных перейдите в браузере по адресу /register или любому другому URL-адресу вашего приложения. Стартовые комплекты возьмут на себя создание всей вашей системы аутентификации!

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

Рекомендации по базе данных

По умолчанию Laravel содержит модель Eloquent App\Models\User в вашем каталоге app/Models. Эта модель использует по умолчанию драйвер аутентификации eloquent. Если ваше приложение не использует Eloquent, то вы можете использовать драйвер аутентификации database, основанный на построителе запросов Laravel.

При построении схемы базы данных для модели App\Models\User убедитесь, что длина столбца password не менее 60 символов. Конечно, миграция таблицы пользователей, включенная в новые приложения Laravel, уже содержит столбец, длина которого превышает эту длину.

Кроме того, вы должны убедиться, что ваша таблица users (или эквивалентная) содержит столбец remember_token с параметрами VARCHAR(100) NULL. Этот столбец будет использоваться для хранения токена для пользователей, которые выбирают опцию «Запомнить меня» при входе в ваше приложение. Опять же, миграция таблицы пользователей по умолчанию, которая включена в новые приложения Laravel, уже содержит этот столбец.

Обзор экосистемы

Laravel предлагает несколько пакетов, связанных с аутентификацией. Прежде чем продолжить, мы рассмотрим общую экосистему аутентификации в Laravel и обсудим предназначение каждого пакета.

Во-первых, рассмотрим, как работает аутентификация. При использовании веб-браузера пользователь вводит свое имя пользователя и пароль через форму входа. Если эти учетные данные верны, то приложение будет хранить информацию об аутентифицированном пользователе в сессии пользователя. Файл cookie, отправленный браузеру, содержит идентификатор сессии, чтобы последующие запросы к приложению могли связать пользователя с правильной сессией. После получения файла cookie сессии, приложение извлекает данные сессии на основе идентификатора сессии, отмечает, что аутентификационная информация была сохранена в сессии, и рассматривает пользователя как «аутентифицированного».

Когда удаленной службе необходимо пройти аутентификации для доступа к API, файлы cookie обычно не используются для проверки подлинности, поскольку веб-браузер отсутствует. Вместо этого удаленная служба отправляет API-токен при каждом запросе к API. Приложение может проверять входящий токен по таблице допустимых API-токенов и «аутентифицировать» запрос как выполняемый пользователем, связанным с этим API-токеном.

Службы Web-аутентификации Laravel из коробки

Laravel содержит встроенные службы аутентификации и сессии, которые обычно доступны через фасады Auth и Session. Этот функционал обеспечивают аутентификацию на основе файлов Cookies для запросов, которые инициируются из веб-браузеров. Они предоставляют методы, которые позволяют вам проверять учетные данные пользователя и аутентифицировать пользователя. Кроме того, эти службы автоматически сохраняют необходимые данные аутентификации в сессии пользователя и выдают cookie сессии пользователя. В этой документации содержится информация о том, как использовать эти службы.

Стартовые комплекты приложения

Как уже было написано в этой документации, вы можете взаимодействовать с этими службами аутентификации напрямую, чтобы создать собственный слой аутентификации вашего приложения. Однако, чтобы помочь вам быстрее приступить к работе, мы выпустили бесплатные пакеты, которые обеспечивают надежную и современную основу всего слоя аутентификации. Это пакеты Laravel Breeze, Laravel Jetstream и Laravel Fortify.

Laravel Breeze – это простая, минимальная реализация всех возможностей аутентификации Laravel, включая вход в систему, регистрацию, сброс пароля, подтверждение электронной почты и подтверждение пароля. Слой представления Laravel Breeze состоит из простых шаблонов Blade, стилизованных с помощью Tailwind CSS. Чтобы начать использование, ознакомьтесь с документацией по стартовым комплектам.

Laravel Fortify – это лишь серверная часть аутентификации для Laravel, которая реализует многие возможности, описанные в этой документации, включая аутентификацию на основе файлов cookie, а также другие возможности, такие как двухфакторная аутентификация и проверка электронной почты. Fortify обеспечивает серверную реализацию аутентификации для Laravel Jetstream, но может использоваться и независимо в сочетании с Laravel Sanctum для обеспечения одностраничных приложений (SPA) возможностью аутентификацией с Laravel.

Laravel Jetstream – это надежный стартовый комплект, который использует и предлагает службы аутентификации Laravel Fortify, но с красивым современным пользовательским интерфейсом на основе Tailwind CSS, Livewire и / или Inertia.js. Laravel Jetstream дополнительно включает поддержку двухфакторной аутентификации, поддержку команды, управление сеансами браузера, управление профилями и встроенную интеграцию с Laravel Sanctum для аутентификации токена API.

Службы API-аутентификации Laravel

Laravel предлагает два дополнительных пакета, которые помогут вам в управлении токенами API и аутентификации запросов, сделанных с помощью токенов API: Passport и Sanctum. Обратите внимание, что эти библиотеки и встроенные в Laravel библиотеки аутентификации на основе файлов cookie не являются взаимоисключающими. Эти библиотеки в основном ориентированы на аутентификацию токена API, в то время как встроенные службы аутентификации ориентированы на web-аутентификацию на основе файлов cookie. Многие приложения будут использовать как встроенные службы аутентификации Laravel на основе файлов cookie, так и один из пакетов API-аутентификации Laravel.

Passport

Passport – это провайдер аутентификации OAuth2, предлагающий различные OAuth2 Grant Types («способы запросы»), которые позволяют вам выдавать различные типы токенов. В общем, это надежный и сложный пакет для аутентификации API. Однако большинству приложений не требуются сложный функционал, предлагаемый спецификацией OAuth2, что может сбивать с толку как пользователей, так и разработчиков. Кроме того, разработчики исторически не понимали, как аутентифицировать приложения SPA или мобильные приложения с помощью провайдеров аутентификации OAuth2, таких, как Passport.

Sanctum

В ответ на сложность OAuth2 и путаницу разработчиков мы решили создать более простой и оптимизированный пакет аутентификации, который мог бы обрабатывать как сторонние веб-запросы из браузера, так и запросы API через токены. Эта цель была реализована с выпуском Laravel Sanctum, который следует считать предпочтительным и рекомендуемым пакетом аутентификации для приложений, и который будет предлагать собственный веб-интерфейс в дополнение к API, или работать с одностраничным приложением (SPA), которое существует отдельно от серверного приложения Laravel, или приложений, предлагающих мобильный клиент.

Laravel Sanctum – это гибридный пакет аутентификации через Web / API, который может управлять всем процессом аутентификации вашего приложения. Это возможно, потому что когда приложения на основе Sanctum получают запрос, Sanctum сначала определяет, содержит ли запрос файл cookie сессии, который ссылается на аутентифицированную сессию. Sanctum выполняет это, вызывая встроенные службы аутентификации Laravel, которые мы обсуждали ранее. Если запрос не аутентифицируется с помощью файла cookie сессии, то Sanctum проверит запрос на наличие токена API. Если присутствует токен API, то Sanctum аутентифицирует запрос с помощью этого токена. Чтобы узнать больше об этом процессе, обратитесь к разделу «Как это работает» документации Sanctum.

Laravel Sanctum – это пакет API, который мы выбрали для включения в стартовый комплект Laravel Jetstream, потому что мы считаем, что он лучше всего подходит для большинства веб-приложений, требующих аутентификации.

Предварительный итог и выбор вашего стека

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

Затем, если ваше приложение предлагает API, который будут использовать третьи стороны, то вы можете выбрать между Passport или Sanctum, чтобы обеспечить аутентификацию токена API для вашего приложения. В целом, по возможности следует отдавать предпочтение Sanctum, поскольку это простое и полное решение для аутентификации API, аутентификации SPA и мобильной аутентификации, включая поддержку «scopes» или «abilities».

Если вы создаете одностраничное приложение (SPA), которое будет работать с серверной частью Laravel, то вам следует использовать Laravel Sanctum. При использовании Sanctum вам потребуется либо самостоятельно реализовать свои собственные маршруты аутентификации на сервере, либо использовать Laravel Fortify как серверную службу аутентификации, которая предлагает маршруты и контроллеры для такого функционала, как регистрация, сброс пароля, подтверждение электронной почты и многое другое.

Passport можно выбрать, если вашему приложению необходим абсолютно весь функционал, предоставляемый спецификацией OAuth2.

И, если вы хотите быстро начать работу, то мы рады порекомендовать пакет Laravel Jetstream как быстрый способ запустить новое приложение Laravel, который уже использует предпочтительный стек аутентификации: встроенные службы аутентификации Laravel и Laravel Sanctum.

Быстрый запуск аутентификации

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

Установка стартовых комплектов

Во-первых, вы должны установить стартовый комплект Laravel. Наши текущие стартовые комплекты, Laravel Breeze и Laravel Jetstream, предлагают красиво оформленные отправные точки для интеграции аутентификации в ваше новое приложение Laravel.

Laravel Breeze – это минимальная и простая реализация всех возможностей аутентификации Laravel, включая вход в систему, регистрацию, сброс пароля, подтверждение электронной почты и подтверждение пароля. Слой представления Laravel Breeze состоит из простых шаблонов Blade, стилизованных с помощью Tailwind CSS.

Laravel Jetstream – это более надежный стартовый комплект для приложений, который включает поддержку построения вашего приложения с помощью Livewire или Inertia.js и Vue. Кроме того, Jetstream предлагает дополнительную поддержку двухфакторной аутентификации, команд, управления профилями, управления сеансами браузера, поддержки API через Laravel Sanctum, удаления аккаунтов и т. д.

Получение аутентифицированного пользователя

После установки стартового аутентификационного комплекта и вашего разрешения пользователям регистрироваться и аутентифицироваться в приложении, вам часто будет требоваться взаимодействовать с текущим аутентифицированным пользователем. При обработке входящего запроса вы можете получить доступ к аутентифицированному пользователю с помощью метода user фасада Auth:

use Illuminate\Support\Facades\Auth;


$user = Auth::user();


$id = Auth::id();

В качестве альтернативы, как только пользователь аутентифицирован, вы можете получить доступ к аутентифицированному пользователю через экземпляр Illuminate\Http\Request. Помните, что объявленные типы зависимостей в методах вашего контроллера будут автоматически внедрены. Объявив объект Illuminate\Http\Request, вы можете получить доступ к аутентифицированному пользователю из любого метода контроллера вашего приложения с помощью метода user запроса:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class FlightController extends Controller
{
    
    public function update(Request $request)
    {
        
    }
}

Определение статуса аутентификации пользователя

Чтобы определить, аутентифицирован ли пользователь, выполняющий входящий HTTP-запрос, вы можете использовать метод check фасада Auth. Этот метод вернет true, если пользователь аутентифицирован:

use Illuminate\Support\Facades\Auth;

if (Auth::check()) {
    
}
Несмотря на то, что можно определить, аутентифицирован ли пользователь с помощью метода check, вы обычно будете использовать посредника для проверки статуса аутентификации пользователя перед предоставлением пользователю доступа к определенным маршрутам / контроллерам. Чтобы узнать больше об этом, ознакомьтесь с документацией по защите маршрутов.

Защита маршрутов

Посредник маршрута используется для того, чтобы разрешить только аутентифицированным пользователям доступ к указанному маршруту. Laravel содержит посредник auth, который ссылается на класс Illuminate\Auth\Middleware\Authenticate. Поскольку этот посредник уже зарегистрирован в HTTP-ядре вашего приложения, все, что вам нужно сделать, это задать посредника к определению маршрута:

Route::get('/flights', function () {
    
})->middleware('auth');

Перенаправление неаутентифицированных пользователей

Когда посредник auth обнаруживает неаутентифицированного пользователя, он перенаправляет пользователя на именованный маршрут login. Вы можете изменить это поведение, обновив функцию redirectTo в файле app/Http/Middleware/Authenticate.php вашего приложения:


protected function redirectTo($request)
{
    return route('login');
}

Указание охранника аутентификации

При задании посредника auth маршруту вы также можете указать, какой «охранник» должен использоваться для аутентификации пользователя. Указанный охранник должен соответствовать одному из указанных в массиве guards конфигурационного файла config/auth.php:

Route::get('/flights', function () {
    
})->middleware('auth:admin');

Частота попыток входа в приложение

Если вы используете стартовые комплекты Laravel Breeze или Laravel Jetstream, то к попыткам входа в систему будет автоматически применяться ограничение. По умолчанию, если пользователь не сможет предоставить правильные учетные данные после нескольких попыток, то он не сможет войти в систему в течение одной минуты. Частота попыток уникальна для имени пользователя / адреса электронной почты и в совокупности с IP-адресом.

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

Самостоятельная реализация аутентификации пользователей

Вам необязательно использовать каркас аутентификации, включенный в стартовые комплекты Laravel. Если вы решите не использовать их, то вам нужно будет управлять аутентификацией пользователей напрямую, используя классы аутентификации Laravel. Не волнуйтесь, но это круто!

Мы получим доступ к службам аутентификации Laravel через фасад Auth, поэтому нам нужно обязательно импортировать фасад Auth в верхней части нашего класса. Далее, давайте проверим метод attempt. Метод attempt обычно используется для обработки попыток аутентификации из формы входа в систему вашего приложения. Если аутентификация прошла успешно, то вы должны повторно создать сессию пользователя, чтобы предотвратить фиксацию сессии:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    
    public function authenticate(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            $request->session()->regenerate();

            return redirect()->intended('dashboard');
        }

        return back()->withErrors([
            'email' => 'The provided credentials do not match our records.',
        ]);
    }
}

Метод attempt принимает массив пар ключ / значение в качестве своего первого аргумента. Значения в массиве будут использоваться для поиска пользователя в таблице базы данных. Итак, в приведенном выше примере пользователь будет извлечен по значению столбца email. Если пользователь найден, то хешированный пароль, хранящийся в базе данных, будет сравниваться со значением password, переданным в метод через массив. Вы не должны хешировать значение пароля входящего запроса, поскольку фреймворк автоматически хеширует это значение, прежде чем сравнивать его с хешированным паролем в базе данных. Если два хешированных пароля совпадают, то для пользователя будет запущена аутентифицированная сессия.

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

Метод attempt вернет true, если аутентификация прошла успешно. В противном случае будет возвращено false.

Метод intended экземпляра Illuminate\Routing\Redirector Laravel, будет перенаправлять пользователя на URL-адрес, к которому он пытался получить доступ, прежде чем он будет перехвачен посредником аутентификации. Этому методу может быть предоставлен резервный URI, в случае, если методу intended переданный адрес недоступен.

Указание дополнительных условий

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

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
    
}
В этих примерах email не является обязательным параметром, он просто используется в качестве примера. Вы должны использовать любое имя столбца, равнозначное «имени пользователя» в таблице базы данных.

Доступ к конкретному экземпляру охранника аутентификации

Используя метод guard фасада Auth, вы можете указать, какой экземпляр охранника вы хотите использовать при аутентификации пользователя. Это позволяет вам управлять аутентификацией для отдельных частей вашего приложения с использованием абсолютно отдельных аутентифицируемых моделей или пользовательских таблиц.

Имя охранника, переданное методу guard, должно соответствовать одному из настроенных в вашем файле конфигурации auth.php охраннику:

if (Auth::guard('admin')->attempt($credentials)) {
    
}

Запоминание пользователей

Многие веб-приложения содержат флажок «Запомнить меня» в форме входа. Если вы хотите реализовать функционал «Запомнить меня» в своем приложении, то вы можете передать логическое значение в качестве второго аргумента метода attempt.

Когда это значение равно true, Laravel будет поддерживать аутентификацию пользователя неопределенно долго или до тех пор, пока он не выйдет из системы вручную. Ваша таблица users должна включать столбец remember_token, который будет использоваться для хранения токена функционала «Запомнить меня». Миграция таблицы пользователей, входящая в новые приложения Laravel, уже содержит этот столбец:

use Illuminate\Support\Facades\Auth;

if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
    
}

Другие методы аутентификации

Аутентификация пользователя по экземпляру модели

Если вам нужно задать экземпляр существующего пользователя в качестве текущего аутентифицированного, то вы можете передать этот экземпляр методу login фасада Auth. Переданный экземпляр пользователя должен быть реализацией контракта Illuminate\Contracts\Auth\Authenticatable. Модель App\Models\User, поставляемая с Laravel, уже реализует этот интерфейс. Этот метод аутентификации полезен, когда у вас уже есть экземпляр пользователя, например, сразу после того, как пользователь регистрируется в вашем приложении:

use Illuminate\Support\Facades\Auth;

Auth::login($user);

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

Auth::login($user, $remember = true);

При необходимости вы можете указать охранника аутентификации перед вызовом метода login:

Auth::guard('admin')->login($user);

Аутентификация пользователя по идентификатору

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

Auth::loginUsingId(1);

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

Auth::loginUsingId(1, $remember = true);

Аутентификация пользователя для текущего запроса

Вы можете использовать метод once для аутентификации пользователя в приложении только для одного запроса. При вызове этого метода не будут использоваться сессии или файлы cookie:

if (Auth::once($credentials)) {
    
}

Basic HTTP-аутентификация

Basic HTTP-аутентификация обеспечивает быстрый способ аутентификации пользователей вашего приложения без создания специальной «страницы входа». Для начала задайте маршруту посредника auth.basic. Посредник auth.basic поставляется с Laravel, поэтому вам не нужно его определять:

Route::get('/profile', function () {
    
})->middleware('auth.basic');

После того как посредник задан маршруту, вам будет автоматически предложено ввести учетные данные при доступе к маршруту в вашем браузере. По умолчанию посредник auth.basic предполагает, что столбец email в вашей таблице базы данных users является его «логином».

Примечание о FastCGI

Если вы используете PHP FastCGI и Apache для своего приложения Laravel, то аутентификация HTTP Basic может работать некорректно. Чтобы исправить эти проблемы, в файл .htaccess вашего приложения можно добавить следующие строки:

RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

Basic HTTP-аутентификация без сохранения состояния

Вы также можете использовать Basic HTTP-аутентификацию без задания cookie идентификатора пользователя в сессии. Это в первую очередь полезно, если вы решите использовать HTTP-аутентификацию для аутентификации запросов к API вашего приложения. Для этого определите посредника, который вызывает метод onceBasic. Если метод onceBasic не возвращает ответа, то запрос может быть передан дальше в приложение:

namespace App\Http\Middleware;

use Illuminate\Support\Facades\Auth;

class AuthenticateOnceWithBasicAuth
{
    
    public function handle($request, $next)
    {
        return Auth::onceBasic() ?: $next($request);
    }

}

Затем зарегистрируйте посредника и задайте его маршруту:

Route::get('/api/user', function () {
    
})->middleware('auth.basic.once');

Выход из приложения

Чтобы обеспечить пользователю возможность выхода из вашего приложения, вы можете использовать метод logout фасада Auth. Это удалит информацию аутентификации из сессии пользователя, так что последующие запросы не будут аутентифицированы.

В дополнение к вызову метода logout рекомендуется аннулировать сессию пользователя и повторно сгенерировать его токен CSRF. После выхода пользователя из системы вы обычно перенаправляете пользователя в корень вашего приложения:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;


public function logout(Request $request)
{
    Auth::logout();

    $request->session()->invalidate();

    $request->session()->regenerateToken();

    return redirect('/');
}

Аннулирование сессий на других устройствах

Laravel также предлагает механизм для «выхода» пользователя и аннулирования сессий, активных на других устройствах, без аннулирования сессии на его текущем устройстве. Этот функционал обычно используется, когда пользователь меняет или обновляет свой пароль, и вы хотите аннулировать сессии на других устройствах, сохранив аутентификацию текущего устройства.

Перед тем как начать, вы должны убедиться, что посредник Illuminate\Session\Middleware\AuthenticateSession присутствует и не закомментирован в вашей группе посредников web класса App\Http\Kernel:

'web' => [
    
    \Illuminate\Session\Middleware\AuthenticateSession::class,
    
],

Затем вы можете использовать метод logoutOtherDevices фасада Auth. Этот метод требует, чтобы пользователь подтвердил свой текущий пароль, который ваше приложение должно принять через форму ввода:

use Illuminate\Support\Facades\Auth;

Auth::logoutOtherDevices($currentPassword);

Когда вызывается метод logoutOtherDevices, другие сессии пользователя будут полностью аннулированы, то есть он будут «отключен» от всех охранников, которым он ранее был аутентифицированы.

Подтверждение пароля

При создании приложения вы можете потребовать от пользователя подтверждения пароля перед выполнением действия или перед перенаправлением пользователя в конфиденциальный раздел приложения. Laravel содержит встроенный посредник для упрощения этого процесса. Реализация этого функционала потребует от вас определения двух маршрутов: один маршрут для отображения шаблона, предлагающего пользователю подтвердить свой пароль, и другой маршрут для уточнения действительности пароля и дальнейшего перенаправления его к необходимому разделу.

В следующей документации обсуждается, как напрямую интегрироваться с функционалом подтверждения пароля Laravel; однако, если вы хотите начать работу быстрее, то стартовые комплекты Laravel уже включают поддержку этого функционала!

Конфигурация подтверждения пароля

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

Маршрутизация подтверждения пароля

Форма подтверждения пароля

Сначала мы определим маршрут для отображения шаблона формы с запросом у пользователя подтверждения своего пароля:

Route::get('/confirm-password', function () {
    return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');

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

Процесс подтверждения пароля

Затем мы определим маршрут, который будет обрабатывать запрос формы из шаблона «подтвердить пароль». Этот маршрут будет отвечать за проверку пароля и перенаправление пользователя к месту назначения:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;

Route::post('/confirm-password', function (Request $request) {
    if (! Hash::check($request->password, $request->user()->password)) {
        return back()->withErrors([
            'password' => ['The provided password does not match our records.']
        ]);
    }

    $request->session()->passwordConfirmed();

    return redirect()->intended();
})->middleware(['auth', 'throttle:6,1'])->name('password.confirm');

Прежде чем двигаться дальше, давайте рассмотрим этот маршрут более подробно. Во-первых, определяется, что поле password запроса действительно соответствует паролю аутентифицированного пользователя. Если пароль действителен, то нам нужно сообщить сессии Laravel, что пользователь подтвердил свой пароль. Метод passwordConfirmed устанавливает временную метку в сессии пользователя, которую Laravel может использовать, чтобы определить, когда пользователь последний раз подтвердил свой пароль. Наконец, мы можем перенаправить пользователя по назначению.

Защита маршрутов

Вы должны убедиться, что любому маршруту, связанному с подтверждением пароля, назначен посредник password.confirm. Этот посредник входит в стандартную установку Laravel и автоматически сохраняет предполагаемое место назначения пользователя в сессии, чтобы пользователя можно было перенаправить в это место после подтверждения своего пароля. После сохранения предполагаемого пункта назначения пользователя в сессии посредник перенаправит пользователя на именованный маршрут password.confirm:

Route::get('/settings', function () {
    
})->middleware(['password.confirm']);

Route::post('/settings', function () {
    
})->middleware(['password.confirm']);

Добавление своих охранников аутентификации

Вы можете определить своих собственных охранников аутентификации, используя метод extend фасада Auth. Вы должны разместить свой вызов метода extend внутри поставщика служб. Поскольку Laravel уже содержит AuthServiceProvider, мы можем разместить код в этом поставщике:

namespace App\Providers;

use App\Services\Auth\JwtGuard;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;

class AuthServiceProvider extends ServiceProvider
{
    
    public function boot()
    {
        $this->registerPolicies();

        Auth::extend('jwt', function ($app, $name, array $config) {
            

            return new JwtGuard(Auth::createUserProvider($config['provider']));
        });
    }
}

Как вы можете видеть в приведенном выше примере, замыкание, переданное методу extend, должно возвращать реализацию Illuminate\Contracts\Auth\Guard. Этот интерфейс содержит несколько методов, которые вам необходимо реализовать для определения своего охранника. После того как ваш охранник был определен, вы можете ссылаться на него в конфигурации guards конфигурационного файла config/auth.php:

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

Анонимные охранники аутентификации на базе HTTP-запросов

Самый простой способ реализовать собственную систему аутентификации на базе HTTP-запросов – использовать метод Auth::viaRequest. Этот метод позволяет быстро определить процесс аутентификации с помощью одного замыкания.

Для начала вызовите метод Auth::viaRequest в методе boot вашего AuthServiceProvider. Метод viaRequest принимает имя драйвера аутентификации в качестве своего первого аргумента. Это имя может быть любой строкой, описывающей вашего охранника. Второй аргумент, передаваемый методу, должно быть замыкание, которое принимает входящий HTTP-запрос и возвращает экземпляр пользователя или, если аутентификация не удалась, то null:

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;


public function boot()
{
    $this->registerPolicies();

    Auth::viaRequest('custom-token', function (Request $request) {
        return User::where('token', $request->token)->first();
    });
}

После того как ваш драйвер аутентификации был определен, вы можете настроить его как драйвер в конфигурации guards конфигурационного файла config/auth.php:

'guards' => [
    'api' => [
        'driver' => 'custom-token',
    ],
],

Добавление своих провайдеров пользователей

Если вы не используете традиционную реляционную базу данных для хранения своих пользователей, то вам нужно будет расширить Laravel своим собственным провайдером аутентификации пользователей. Мы будем использовать метод provider фасада Auth для определения собственного провайдера пользователей. Провайдер пользователей должен вернуть реализацию Illuminate\Contracts\Auth\UserProvider:

namespace App\Providers;

use App\Extensions\MongoUserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;

class AuthServiceProvider extends ServiceProvider
{
    
    public function boot()
    {
        $this->registerPolicies();

        Auth::provider('mongo', function ($app, array $config) {
            

            return new MongoUserProvider($app->make('mongo.connection'));
        });
    }
}

После того как вы зарегистрировали провайдера с помощью метода provider, вы можете переключиться на нового провайдера пользователей в конфигурационном файле config/auth.php. Сначала определите провайдера, который использует ваш новый драйвер:

'providers' => [
    'users' => [
        'driver' => 'mongo',
    ],
],

Наконец, вы можете указать этого провайдера в своей конфигурации guards:

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

Контракт UserProvider

Реализации Illuminate\Contracts\Auth\UserProvider отвечают за получение реализации Illuminate\Contracts\Auth\Authenticatable из системы постоянного хранения, такой как MySQL, MongoDB и т. д. Эти два интерфейса позволяют механизмам аутентификации Laravel продолжать функционировать независимо от того, как хранятся пользовательские данные или какой тип класса используется для представления аутентифицированного пользователя.

Давайте посмотрим на контракт Illuminate\Contracts\Auth\UserProvider:

namespace Illuminate\Contracts\Auth;

interface UserProvider
{
    public function retrieveById($identifier);
    public function retrieveByToken($identifier, $token);
    public function updateRememberToken(Authenticatable $user, $token);
    public function retrieveByCredentials(array $credentials);
    public function validateCredentials(Authenticatable $user, array $credentials);
}
  • Метод retrieveById обычно принимает ключ, представляющий пользователя, такой как автоинкрементный идентификатор из базы данных MySQL. Реализация Authenticatable, соответствующая идентификатору, должна быть получена и возвращена методом.

  • Метод retrieveByToken извлекает пользователя по его уникальному идентификатору $identifier и $token, обычно хранящимся в столбце remember_token базы данных. Как и в предыдущем методе, этот метод должен вернуть реализацию Authenticatable соответствующую значению токена.

  • Метод updateRememberToken обновляет remember_token экземпляра $user новым $token. Новый токен назначается пользователям при успешной попытке аутентификации с отмеченным флажком «Запомнить меня» или когда пользователь выходит из системы.

  • Метод retrieveByCredentials принимает массив учетных данных, переданный методу Auth::attempt при попытке аутентификации в приложении. Затем метод должен «запросить» у постоянного хранилища пользователя, соответствующего этим учетным данным. Как правило, этот метод запускает запрос с условием WHERE, который ищет запись пользователя с «именем пользователя», равнозначным $credentials['имя пользователя']. Метод должен возвращать реализацию Authenticatable. Этот метод не должен пытаться выполнить проверку пароля или аутентификацию.

  • Метод validateCredentials должен сравнивать переданный $user с $credentials для аутентификации пользователя. Например, этот метод обычно использует метод Hash::check для сравнения значения $user->getAuthPassword() со значением $credentials['password']. Этот метод должен возвращать true или false, указывая, действителен ли пароль.

Контракт Authenticatable

Теперь, когда мы изучили каждый из методов UserProvider, давайте взглянем на контракт Authenticatable. Помните, что провайдеры пользователей должны возвращать реализации этого интерфейса из методов retrieveById, retrieveByToken, и retrieveByCredentials:

namespace Illuminate\Contracts\Auth;

interface Authenticatable
{
    public function getAuthIdentifierName();
    public function getAuthIdentifier();
    public function getAuthPassword();
    public function getRememberToken();
    public function setRememberToken($value);
    public function getRememberTokenName();
}

Этот интерфейс прост. Метод getAuthIdentifierName должен возвращать имя поля «первичного ключа» пользователя, а метод getAuthIdentifier должен возвращать «первичный ключ» пользователя. При использовании серверной части MySQL это, вероятно, будет автоинкрементный первичный ключ, присваиваемый записи пользователя. Метод getAuthPassword должен возвращать хешированный пароль пользователя.

Этот интерфейс позволяет системе аутентификации работать с любым классом User, независимо от того, какой ORM или уровень абстракции хранилища вы используете. По умолчанию Laravel включает класс App\Models\User в каталог app/Models, который реализует этот интерфейс.

Создайте новое приложение

Давайте построим новый. Laravel Приложение. Выполните следующие команды, чтобы создать совершенно новое приложение laravel .

composer create-project --prefer-dist laravel/laravel passport

установить продление паспорта

Мы используем композитор установка Паспорт Расширение. Выполните следующую команду, чтобы установить расширение.

composer require laravel/passport

Laravel Для настройки Паспорта

Laravel Паспорт Расширение требует некоторой настройки.

Поставщик услуг

Что мы используем Laravel 5.6 В последней версии он может использовать обнаружение пакетов и автоматически регистрировать службы. Если вы используете Laravel 5.4 или ниже Вам необходимо быть в ____________ конфигурация/приложение. php В документе Паспорт Регистрационная служба. Вот и все. Это есть в этом файле. провайдеры Добавляют услуги регистрации в массив.

'providers' => [
    ....
    Laravel\Passport\PassportServiceProvider::class,
]

Миграция и установка

оставайтесь .env Установите учетные данные базы данных в файле. Laravel Passport предоставляет файлы миграции, необходимые для таблиц паспортов в нашей базе данных. Миграция паспортов используется для хранения токенов и информации о клиентах. Функция миграция Команда для переноса схемы в базу данных.

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

php artisan passport:install

Конфигурация паспорта

На этом шаге нам нужно Laravel Изменить в приложении, чтобы заполнить Паспорт Для настройки.

app/User.php

В вашем Пользователе Добавьте в модель LaravelPassportHasApiTokens Трат. Это обеспечит некоторые вспомогательные методы.

AuthServiceProvider

оставайтесь AuthServiceProvider Добавьте метод загрузки Passport:: routes() Метод. Он генерирует необходимую маршрутизацию. Это app/Providers/AuthServiceProvider.php Как это выглядит после изменения.

 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

config/auth.php

В config/auth.php В файле установите для драйвера значение паспорт.

return [
    ....

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

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

    ....
]

Создание маршрута

Давайте создадим маршрутизацию API, в routes/api.php Добавить маршрутизацию.

Создание контроллера аутентификации

Давайте настроим логику аутентификации. Создайте контроллер паспорта, выполнив следующую команду.

php artisan make:controller PassportController

Скопируйте следующий код в app/Http/Controllers/PassportController.php

validate($request, [
            'name' => 'required|min:3',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6',
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password)
        ]);

        $token = $user->createToken('TutsForWeb')->accessToken;

        return response()->json(['token' => $token], 200);
    }

    /**
     * Handles Login Request
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        $credentials = [
            'email' => $request->email,
            'password' => $request->password
        ];

        if (auth()->attempt($credentials)) {
            $token = auth()->user()->createToken('TutsForWeb')->accessToken;
            return response()->json(['token' => $token], 200);
        } else {
            return response()->json(['error' => 'UnAuthorised'], 401);
        }
    }

    /**
     * Returns Authenticated User Details
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function details()
    {
        return response()->json(['user' => auth()->user()], 200);
    }
}

Позвольте мне объяснить приведенный выше код.

В регистрация В методе мы проверяем данные запроса, а затем создаем пользователя. Мы используем метод createToken , который создает токен и передает имя в качестве параметра. Наконец, мы возвращаем токен в ответе JSON.

В логин В этом методе мы пытаемся аутентифицироваться с помощью параметров запроса. Затем возвращается соответствующий ответ в зависимости от успеха или неудачи попытки.

В подробности В этом методе мы возвращаем только модель пользователя.

Создать продукт СЫРОЙ

Давайте создадим продукт CRUD. Выполните следующие команды для создания моделей продуктов, файлов миграции и контроллеров.

php artisan make:model Product -mc

Это создаст новый файл миграции базы данных create_products_table.php (2) В случае ____________ база данных/миграции Папка. Будет вверх Метод обновлен до следующего кода.

public function up()
{
    Schema::create('products', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id');
        $table->string('name');
        $table->integer('price');
        $table->timestamps();

        $table->foreign('user_id')
            ->references('id')
            ->on('users');
    });
}

Теперь добавьте заполняемый Атрибут в Продукт Модель. Откройте appy В папке Product.php Документация.

Теперь мы запускаем миграцию данных.

А теперь давайте побудем в ___________. app/User.php Добавьте метод ассоциации в файл.

public function products()
{
    return $this->hasMany(Product::class);
}

Откройте приложение/Http/Контроллеры В папке ProductController.php Документация. Скопируйте следующий код в контроллер продукта.

user()->products;

        return response()->json([
            'success' => true,
            'data' => $products
        ]);
    }

    public function show($id)
    {
        $product = auth()->user()->products()->find($id);

        if (!$product) {
            return response()->json([
                'success' => false,
                'message' => 'Product with id ' . $id . ' not found'
            ], 400);
        }

        return response()->json([
            'success' => true,
            'data' => $product->toArray()
        ], 400);
    }

    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required',
            'price' => 'required|integer'
        ]);

        $product = new Product();
        $product->name = $request->name;
        $product->price = $request->price;

        if (auth()->user()->products()->save($product))
            return response()->json([
                'success' => true,
                'data' => $product->toArray()
            ]);
        else
            return response()->json([
                'success' => false,
                'message' => 'Product could not be added'
            ], 500);
    }

    public function update(Request $request, $id)
    {
        $product = auth()->user()->products()->find($id);

        if (!$product) {
            return response()->json([
                'success' => false,
                'message' => 'Product with id ' . $id . ' not found'
            ], 400);
        }

        $updated = $product->fill($request->all())->save();

        if ($updated)
            return response()->json([
                'success' => true
            ]);
        else
            return response()->json([
                'success' => false,
                'message' => 'Product could not be updated'
            ], 500);
    }

    public function destroy($id)
    {
        $product = auth()->user()->products()->find($id);

        if (!$product) {
            return response()->json([
                'success' => false,
                'message' => 'Product with id ' . $id . ' not found'
            ], 400);
        }

        if ($product->delete()) {
            return response()->json([
                'success' => true
            ]);
        } else {
            return response()->json([
                'success' => false,
                'message' => 'Product could not be deleted'
            ], 500);
        }
    }
}

Создание заготовок

Для создания заготовок всех необходимых для аутентификации контроллеров, шаблонов и роутов предназначен пакет laravel/ui:

> composer require laravel/ui --dev
> php artisan ui:auth
> npm install && npm run dev

Эту команду нужно использовать сразу после установки приложения laravel. Кроме роутов и шаблонов, будет создан контроллер HomeController, на который будет выполняться редирект после успешной аутентификации.

На этапе создания приложения можно указать флаг --auth и инсталлятор сразу добавит систему аутентификации:

> composer global require "laravel/installer"
> laravel new blog --auth # новое приложение

Будут созданы контроллеры:

  • RegisterController — обеспечивает регистрацию пользователей
  • LoginController — обеспечивает аутентификацию пользователей
  • ForgotPasswordController — отправляет письмо на сброс пароля
  • ResetPasswordController — содержит логику для сброса паролей

Будут созданы шаблоны:

  • auth.register — форма регистрации пользователей
  • auth.login — форма аутентификации пользователей
  • auth.passwords.email — форма для ввода адреса почты (восстановление пароля)
  • auth.passwords.reset — форма для ввода нового пароля (восстановление пароля)

Будут добавлены маршруты:

Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');

Вызов Auth::routes() добавляет сразу около дюжины маршрутов, посмотреть эти маршруты можно в классе Laravel\Ui\AuthRouteMethods.

Заготовки контроллеров

Контроллер RegisterController в сочетании с трейтом RegistersUsers показывает форму регистрации, проводит валидацию данных и создает нового пользователя и выполняет редирект на страницу server.com/home. Свойство $redirectTo определяет, куда будет перенаправлен пользователь после регистрации. Метод validator() определяет способ валидации регистрационных данных. А метод create() — как создать нового пользователя на основе полученных регистрационных данных.

namespace App\Http\Controllers\Auth;

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

class RegisterController extends Controller {

    use RegistersUsers;

    protected $redirectTo = RouteServiceProvider::HOME;

    public function __construct() {
        $this->middleware('guest');
    }

    protected function validator(array $data) {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

    protected function create(array $data) {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}
namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Events\Registered;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

trait RegistersUsers {

    use RedirectsUsers;

    public function showRegistrationForm() {
        return view('auth.register');
    }

    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 JsonResponse([], 201)
                    : redirect($this->redirectPath());
    }

    protected function guard() {
        return Auth::guard();
    }

    protected function registered(Request $request, $user) {
        // .....
    }
}

Контроллер LoginController позволяет пользователю выполнить вход в систему. Он подружает трейт AuthenticatesUsers, подгружает RedirectUsers и ThrottlesLogins. Свойство $redirectTo определяет, куда будет перенаправлен пользователь после аутентификации.

namespace App\Http\Controllers\Auth;

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

class LoginController extends Controller {

    use AuthenticatesUsers;

    protected $redirectTo = RouteServiceProvider::HOME;

    public function __construct() {
        $this->middleware('guest')->except('logout');
    }
}
namespace Illuminate\Foundation\Auth;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

trait AuthenticatesUsers {

    use RedirectsUsers, ThrottlesLogins;

    public function showLoginForm() {
        return view('auth.login');
    }

    public function login(Request $request) {
        $this->validateLogin($request);

        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    protected function validateLogin(Request $request) {
        $request->validate([
            $this->username() => 'required|string',
            'password' => 'required|string',
        ]);
    }

    protected function attemptLogin(Request $request) {
        return $this->guard()->attempt(
            $this->credentials($request), $request->filled('remember')
        );
    }

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

    protected function sendLoginResponse(Request $request) {
        $request->session()->regenerate();

        $this->clearLoginAttempts($request);

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

        return $request->wantsJson()
                    ? new JsonResponse([], 204)
                    : redirect()->intended($this->redirectPath());
    }

    protected function authenticated(Request $request, $user) {
        // .....
    }

    protected function sendFailedLoginResponse(Request $request) {
        throw ValidationException::withMessages([
            $this->username() => [trans('auth.failed')],
        ]);
    }

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

    public function logout(Request $request) {
        $this->guard()->logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        if ($response = $this->loggedOut($request)) {
            return $response;
        }

        return $request->wantsJson()
            ? new JsonResponse([], 204)
            : redirect('/');
    }

    protected function loggedOut(Request $request) {
        // .....
    }

    protected function guard() {
        return Auth::guard();
    }
}

Трейт ThrottlesLogins запрещает использовать форму входа пользователям с большим количеством неудачных попыток входа.

Контроллер ResetPasswordController подгружает трейт ResetsPasswords. Этот трейт показывает форму сброса пароля (метод showResetForm()), обрабатывает POST-запрос, выполянет валидацию и сбрасывает пароль.

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;

class ResetPasswordController extends Controller {

    use ResetsPasswords;

    protected $redirectTo = RouteServiceProvider::HOME;
}
namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

trait ResetsPasswords {

    use RedirectsUsers;

    public function showResetForm(Request $request, $token = null) {
        return view('auth.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }

    public function reset(Request $request) {
        $request->validate($this->rules(), $this->validationErrorMessages());

        // Here we will attempt to reset the user's password. If it is successful we
        // will update the password on an actual user model and persist it to the
        // database. Otherwise we will parse the error and return the response.
        $response = $this->broker()->reset(
            $this->credentials($request), function ($user, $password) {
                $this->resetPassword($user, $password);
            }
        );

        // If the password was successfully reset, we will redirect the user back to
        // the application's home authenticated view. If there is an error we can
        // redirect them back to where they came from with their error message.
        return $response == Password::PASSWORD_RESET
                    ? $this->sendResetResponse($request, $response)
                    : $this->sendResetFailedResponse($request, $response);
    }

    protected function rules() {
        return [
            'token' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed|min:8',
        ];
    }

    protected function validationErrorMessages() {
        return [];
    }

    protected function credentials(Request $request) {
        return $request->only(
            'email', 'password', 'password_confirmation', 'token'
        );
    }

    protected function resetPassword($user, $password) {
        $this->setUserPassword($user, $password);
        $user->setRememberToken(Str::random(60));
        $user->save();
        event(new PasswordReset($user));
        $this->guard()->login($user);
    }

    protected function setUserPassword($user, $password) {
        $user->password = Hash::make($password);
    }

    protected function sendResetResponse(Request $request, $response) {
        if ($request->wantsJson()) {
            return new JsonResponse(['message' => trans($response)], 200);
        }

        return redirect($this->redirectPath())
                            ->with('status', trans($response));
    }

    protected function sendResetFailedResponse(Request $request, $response) {
        if ($request->wantsJson()) {
            throw ValidationException::withMessages([
                'email' => [trans($response)],
            ]);
        }

        return redirect()->back()
                    ->withInput($request->only('email'))
                    ->withErrors(['email' => trans($response)]);
    }

    public function broker() {
        return Password::broker();
    }

    protected function guard() {
        return Auth::guard();
    }
}

Контроллер ForgotPasswordController подгружает трейт SendsPasswordResetEmails. Он показывает форму с помощью метода showLinkRequestForm, обрабатывает POST-запрос с помощию метода sendResetLinkEmail.

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller {

    use SendsPasswordResetEmails;
}
namespace Illuminate\Foundation\Auth;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;

trait SendsPasswordResetEmails {

    public function showLinkRequestForm() {
        return view('auth.passwords.email');
    }

    public function sendResetLinkEmail(Request $request) {
        $this->validateEmail($request);

        // We will send the password reset link to this user. Once we have attempted
        // to send the link, we will examine the response then see the message we
        // need to show to the user. Finally, we'll send out a proper response.
        $response = $this->broker()->sendResetLink(
            $this->credentials($request)
        );

        return $response == Password::RESET_LINK_SENT
                    ? $this->sendResetLinkResponse($request, $response)
                    : $this->sendResetLinkFailedResponse($request, $response);
    }

    protected function validateEmail(Request $request) {
        $request->validate(['email' => 'required|email']);
    }

    protected function credentials(Request $request) {
        return $request->only('email');
    }

    protected function sendResetLinkResponse(Request $request, $response) {
        return $request->wantsJson()
                    ? new JsonResponse(['message' => trans($response)], 200)
                    : back()->with('status', trans($response));
    }

    protected function sendResetLinkFailedResponse(Request $request, $response) {
        if ($request->wantsJson()) {
            throw ValidationException::withMessages([
                'email' => [trans($response)],
            ]);
        }

        return back()
                ->withInput($request->only('email'))
                ->withErrors(['email' => trans($response)]);
    }

    public function broker() {
        return Password::broker();
    }
}

Контроллер HomeController — после ввода логина и пароля пользователь будет отправлен на эту страницу:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller {

    public function __construct() {
        $this->middleware('auth');
    }

    public function index() {
        return view('home');
    }
}

Заготовки шаблонов

Шаблон формы для регистрации resources/views/auth/register.blade.php:

@extends('layouts.app')

@section('content')
class="container">
class="row justify-content-center">
class="col-md-8">
class="card">
class="card-header">{{ __('Register') }}
class="card-body">
method
="POST" action="{{ route('register') }}"> @csrf
class="form-group row"> ="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}
class="col-md-6"> id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus> @error('name') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row"> ="email" class="col-md-4 col-form-label text-md-right"> {{ __('E-Mail Address') }}
class="col-md-6"> id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row"> ="password" class="col-md-4 col-form-label text-md-right"> {{ __('Password') }}
class="col-md-6"> id="password" type="password" name="password" autocomplete="new-password" required class="form-control @error('password') is-invalid @enderror"> @error('password') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row"> ="password-confirm" class="col-md-4 col-form-label text-md-right"> {{ __('Confirm Password') }}
class="col-md-6"> id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
class="form-group row mb-0">
class="col-md-6 offset-md-4"> ="submit" class="btn btn-primary"> {{ __('Register') }}
@endsection

Шаблон формы для аутентификации (ввод логина и пароля) resources/views/auth/login.blade.php:

@extends('layouts.app')

@section('content')
class="container">
class="row justify-content-center">
class="col-md-8">
class="card">
class="card-header">{{ __('Login') }}
class="card-body">
method
="POST" action="{{ route('login') }}"> @csrf
class="form-group row"> ="email" class="col-md-4 col-form-label text-md-right"> {{ __('E-Mail Address') }}
class="col-md-6"> id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row"> ="password" class="col-md-4 col-form-label text-md-right"> {{ __('Password') }}
class="col-md-6"> id="password" type="password" name="password" autocomplete="current-password" required class="form-control @error('password') is-invalid @enderror"> @error('password') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row">
class="col-md-6 offset-md-4">
class="form-check"> class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> ="form-check-label" for="remember"> {{ __('Remember Me') }}
class="form-group row mb-0">
class="col-md-8 offset-md-4"> ="submit" class="btn btn-primary"> {{ __('Login') }} @if (Route::has('password.request')) class="btn btn-link" href="{{ route('password.request') }}"> {{ __('Forgot Your Password?') }} @endif
@endsection

Шаблон формы для ввода адреса почты при сбросе пароля resources/views/auth/password/reset.blade.php:

@extends('layouts.app')

@section('content')
class="container">
class="row justify-content-center">
class="col-md-8">
class="card">
class="card-header">{{ __('Reset Password') }}
class="card-body">
method
="POST" action="{{ route('password.update') }}"> @csrf type="hidden" name="token" value="{{ $token }}">
class="form-group row"> ="email" class="col-md-4 col-form-label text-md-right"> {{ __('E-Mail Address') }}
class="col-md-6"> id="email" type="email" name="email" value="{{ $email ?? old('email') }}" class="form-control @error('email') is-invalid @enderror" required autocomplete="email" autofocus> @error('email') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row"> ="password" class="col-md-4 col-form-label text-md-right"> {{ __('Password') }}
class="col-md-6"> id="password" type="password" name="password" autocomplete="new-password" required class="form-control @error('password') is-invalid @enderror"> @error('password') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row"> ="password-confirm" class="col-md-4 col-form-label text-md-right"> {{ __('Confirm Password') }}
class="col-md-6"> id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
class="form-group row mb-0">
class="col-md-6 offset-md-4"> ="submit" class="btn btn-primary"> {{ __('Reset Password') }}
@endsection

Шаблон формы для ввода нового пароля при сбросе пароля resources/views/auth/password/email.blade.php:

@extends('layouts.app')

@section('content')
class="container">
class="row justify-content-center">
class="col-md-8">
class="card">
class="card-header">{{ __('Reset Password') }}
class="card-body"> @if (session('status'))
class="alert alert-success" role="alert"> {{ session('status') }}
@endif
method
="POST" action="{{ route('password.email') }}"> @csrf
class="form-group row"> ="email" class="col-md-4 col-form-label text-md-right"> {{ __('E-Mail Address') }}
class="col-md-6"> id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') class="invalid-feedback" role="alert"> {{ $message }} @enderror
class="form-group row mb-0">
class="col-md-6 offset-md-4"> ="submit" class="btn btn-primary"> {{ __('Send Password Reset Link') }}
@endsection

Добавленные маршруты

После выполнения artisan-команды, будут добавлены всего две строки в файл routes/web.php:

Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');

Вызов Auth::routes() добавляет сразу около дюжины маршрутов, посмотреть эти маршруты можно в классе Laravel\Ui\AuthRouteMethods.

namespace Laravel\Ui;

class AuthRouteMethods {
    public function auth() {
        // authentication routes
        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();
            }
        };
    }

    public function resetPassword() {
        // reset password routes
        return function () {
            $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
            $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
            $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
            $this->post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
        };
    }

    public function confirmPassword() {
        // confirm password routes
        return function () {
            $this->get('password/confirm', 'Auth\ConfirmPasswordController@showConfirmForm')->name('password.confirm');
            $this->post('password/confirm', 'Auth\ConfirmPasswordController@confirm');
        };
    }

    public function emailVerification() {
        // email verification routes
        return function () {
            $this->get('email/verify', 'Auth\VerificationController@show')->name('verification.notice');
            $this->get('email/verify/{id}/{hash}', 'Auth\VerificationController@verify')->name('verification.verify');
            $this->post('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');
        };
    }
}

Все готово, можно проверять

Теперь пользователи могут регистрироваться и аутентифицироваться. Если открыть в браузере страницу http://server.com/register, то будет показана форма регистрации нового пользователя.

После успешной аутентификации пользователь будет отправлен на страницу http://server.com/home:

Форма для входа в систему будет расположена по адресу http://server.com/login:

Форма для восстановления пароля будет расположена по адресу http://server.com/password/reset:

Перевод на русский язык

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

> composer require laravel-lang/lang:~7.0

После этого скопировать директорию vendor/laravel-lang/lang/src/ru и файл vendor/laravel-lang/lang/json/ru.json в директорию resources/lang.

Файл resources/lang/ru/auth.php

return [
    /*
    |--------------------------------------------------------------------------
    | Языковые ресурсы аутентификации
    |--------------------------------------------------------------------------
    |
    | Следующие языковые ресурсы используются во время аутентификации для
    | различных сообщений которые мы должны вывести пользователю на экран.
    | Вы можете свободно изменять эти языковые ресурсы в соответствии
    | с требованиями вашего приложения.
    |
    */

    'failed'   => 'Неверное имя пользователя или пароль.',
    'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте еще раз через :seconds секунд.',
];

Файл resources/lang/ru/passwords.php

return [
    /*
    |--------------------------------------------------------------------------
    | Языковые ресурсы напоминания пароля
    |--------------------------------------------------------------------------
    |
    | Последующие языковые строки возвращаются брокером паролей на неудачные
    | попытки обновления пароля в таких случаях, как ошибочный код сброса
    | пароля или неверный новый пароль.
    |
    */

    'reset'     => 'Ваш пароль был сброшен!',
    'sent'      => 'Ссылка на сброс пароля была отправлена!',
    'throttled' => 'Пожалуйста, подождите перед повторной попыткой.',
    'token'     => 'Ошибочный код сброса пароля.',
    'user'      => 'Не удалось найти пользователя с указанным электронным адресом.',
];

Файл resources/lang/ru.json

{
  "A fresh verification link has been sent to your email address.": "Новая ссылка для подтверждения была отправлена на Ваш email-адрес.",
  "All rights reserved.": "Все права защищены.",
  "Before proceeding, please check your email for a verification link.": "Прежде чем продолжить, пожалуйста, проверьте ссылку подтверждения в своей электронной почте.",
  "click here to request another": "нажмите здесь, чтобы запросить еще раз",
  "Confirm Password": "Подтверждение пароля",
  "Dashboard": "Главная",
  "E-Mail Address": "Адрес почты",
  "Error": "Ошибка",
  "Forbidden": "Запрещено",
  "Forgot Your Password?": "Забыли пароль?",
  "Go Home": "Домой",
  "Hello!": "Здравствуйте!",
  "hi": "привет",
  "If you did not create an account, no further action is required.": "Если Вы не создавали учетную запись, никаких дополнительных действий не требуется.",
  "If you did not receive the email": "Если Вы не получили письмо",
  "If you did not request a password reset, no further action is required.": "Если Вы не запрашивали сброс пароля, то дополнительных действий не требуется.",
  "If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Если у Вас возникли проблемы с кликом по кнопке \":actionText\", скопируйте и вставьте адрес\nв адресную строку браузера:",
  "Invalid signature.": "Неверная подпись.",
  "Login": "Войти",
  "Logout": "Выйти",
  "Name": "Имя",
  "Not Found": "Не найдено",
  "Oh no": "О, нет",
  "Page Expired": "Страница устарела",
  "Page Not Found": "Страница не найдена",
  "Password": "Пароль",
  "Please click the button below to verify your email address.": "Пожалуйста, нажмите кнопку ниже, чтобы подтвердить свой адрес электронной почты.",
  "Please confirm your password before continuing.": "Пожалуйста, подтвердите Ваш пароль перед продолжением.",
  "Regards": "С уважением",
  "Register": "Регистрация",
  "Remember Me": "Запомнить меня",
  "Reset Password": "Сбросить пароль",
  "Reset Password Notification": "Уведомление сброса пароля",
  "Send Password Reset Link": "Отправить ссылку для сброса пароля",
  "Server Error": "Ошибка сервера",
  "Service Unavailable": "Сервис недоступен",
  "This action is unauthorized.": "Неавторизованное действие.",
  "Sorry, the page you are looking for could not be found.": "Извините, страница, которую вы ищете, не найдена.",
  "Sorry, you are forbidden from accessing this page.": "Извините, вам запрещено пользоваться этой страницей.",
  "Sorry, you are making too many requests to our servers.": "Извините, вы делаете слишком много запросов на наши серверы.",
  "Sorry, you are not authorized to access this page.": "К сожалению, у вас нет доступа к этой странице.",
  "Sorry, your session has expired. Please refresh and try again.": "К сожалению, срок действия вашей сессии истек. Обновите и повторите попытку.",
  "Sorry, we are doing some maintenance. Please check back soon.": "Извините, мы проводим некоторые работы. Пожалуйста, зайдите позже.",
  "This password reset link will expire in :count minutes.": "Срок действия ссылки для сброса пароля истекает через :count минут.",
  "Toggle navigation": "Переключить навигацию",
  "Too Many Attempts.": "Слишком много попыток.",
  "Too Many Requests": "Слишком много запросов",
  "Unauthorized": "Не авторизован",
  "Verify Email Address": "Подтвердить email-адрес",
  "Verify Your Email Address": "Подтвердите Ваш email-адрес",
  "We won't ask for your password again for a few hours.": "Мы не будем запрашивать Ваш пароль вновь в течение нескольких часов.",
  "You are logged in!": "Вы вошли в систему!",
  "You are receiving this email because we received a password reset request for your account.": "Вы получили это письмо, потому что мы получили запрос на сброс пароля для Вашей учетной записи.",
  "Whoops!": "Упс!",
  "Your email address is not verified.": "Ваш email адрес не подтвержден.",
  "Whoops, something went wrong on our servers.": "Упс, на наших серверах что-то пошло не так."
}

Введение

Помимо встроенных служб аутентификации, Laravel также предлагает простой способ авторизации действий пользователя с конкретными ресурсами. Например, даже если пользователь аутентифицирован, то он может быть не авторизован для обновления или удаления определенных моделей Eloquent или записей базы данных вашего приложения. Функционал авторизации Laravel обеспечивает простой и организованный способ управления этими проверками авторизации.

Laravel предлагает два основных способа авторизации действий: шлюзы и политики. Думайте о шлюзах и политиках, как о маршрутах и контроллерах. Шлюзы обеспечивают простой подход к авторизации, основанный на замыкании, в то время как политики, такие как контроллеры, группируют логику вокруг конкретной модели или ресурса. В этой документации мы сначала рассмотрим шлюзы, а затем рассмотрим политики.

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

Шлюзы (Gate)

Написание шлюзов

Шлюзы – отличный способ изучить основы функционала авторизации Laravel; однако при создании надежных приложений Laravel, вам следует рассмотреть возможность использования политик для организации ваших правил авторизации.

Шлюз – это просто замыкание, которое определяет, имеет ли пользователь право выполнять указанное действие. Обычно шлюзы определяются в методе boot класса App\Providers\AuthServiceProvider с использованием фасада Gate. Шлюзы всегда получают экземпляр пользователя в качестве своего первого аргумента и могут получать дополнительные аргументы, например, модель Eloquent.

В этом примере мы определим шлюз, решающий, может ли пользователь обновить указанную модель App\Models\Post. Шлюз выполнит это, сравнив идентификатор пользователя с идентификатором user_id пользователя, создавшего пост:

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;


public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}

Шлюзы также могут быть определены с использованием callback-массива:

use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;


public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', [PostPolicy::class, 'update']);
}

Авторизация действий через шлюзы

Чтобы авторизовать действие с помощью шлюзов, вы должны использовать методы allows или denies фасада Gate. Обратите внимание, что вам не требуется передавать в эти методы аутентифицированного в данный момент пользователя. Laravel автоматически позаботится о передаче пользователя в замыкание шлюза. Обычно методы авторизации шлюза вызываются в контроллерах вашего приложения перед выполнением действия, требующего авторизации:

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    
    public function update(Request $request, Post $post)
    {
        if (! Gate::allows('update-post', $post)) {
            abort(403);
        }

        
    }
}

Если вы хотите определить, авторизован ли другой (не аутентифицированный в настоящий момент) пользователь для выполнения действия, то вы можете использовать метод forUser фасада Gate:

if (Gate::forUser($user)->allows('update-post', $post)) {
    
}

if (Gate::forUser($user)->denies('update-post', $post)) {
    
}

Вы можете определить авторизацию нескольких действий одновременно, используя методы any или none:

if (Gate::any(['update-post', 'delete-post'], $post)) {
    
}

if (Gate::none(['update-post', 'delete-post'], $post)) {
    
}

Авторизация или выброс исключений

Если вы хотите попытаться авторизовать действие и автоматически выбросить исключение Illuminate\Auth\Access\AuthorizationException при не авторизованном действии пользователя, то вы можете использовать метод authorize фасада Gate. Экземпляры AuthorizationException автоматически преобразуются в 403 HTTP-ответ обработчиком исключений Laravel:

Gate::authorize('update-post', $post);


Предоставление дополнительного контекста шлюзам

Методы шлюза для авторизации полномочий (allows, denies, check, any, none, authorize, can, cannot) и директивы авторизации Blade (@can, @cannot, @canany) могут получать массив в качестве второго аргумента. Эти элементы массива передаются в качестве параметров замыканию шлюза и могут использоваться как дополнительный контекст при принятии решений об авторизации:

use App\Models\Category;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('create-post', function (User $user, Category $category, $pinned) {
    if (! $user->canPublishToGroup($category->group)) {
        return false;
    } elseif ($pinned && ! $user->canPinPosts()) {
        return false;
    }

    return true;
});

if (Gate::check('create-post', [$category, $pinned])) {
    
}

Ответы шлюза

До сих пор мы рассматривали шлюзы, возвращающие простые логические значения. По желанию можно вернуть более подробный ответ, содержащий также сообщение об ошибке. Для этого вы можете вернуть экземпляр Illuminate\Auth\Access\Response из вашего шлюза:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::deny('Вы должны быть администратором.');
});

Даже когда вы возвращаете ответ авторизации из вашего шлюза, метод Gate::allows все равно будет возвращать простое логическое значение; однако вы можете использовать метод Gate::inspect, чтобы получить полный возвращенный шлюзом ответ авторизации:

$response = Gate::inspect('edit-settings');

if ($response->allowed()) {
    
} else {
    echo $response->message();
}

При использовании метода Gate::authorize, который генерирует исключение AuthorizationException при не авторизованном действие, сообщение об ошибке ответа авторизации будет передано в HTTP-ответ:

Gate::authorize('edit-settings');


Хуки шлюзов

Иногда бывает необходимо предоставить все полномочия конкретному пользователю. Вы можете использовать метод before для определения замыкания, которое выполняется перед всеми другими проверками авторизации:

use Illuminate\Support\Facades\Gate;

Gate::before(function ($user, $ability) {
    if ($user->isAdministrator()) {
        return true;
    }
});

Если замыкание before возвращает результат, отличный от null, то этот результат и будет считаться результатом проверки авторизации.

Вы можете использовать метод after для определения замыкания, которое будет выполнено после всех других проверок авторизации:

Gate::after(function ($user, $ability, $result, $arguments) {
    if ($user->isAdministrator()) {
        return true;
    }
});

Подобно методу before, если замыкание after возвращает результат, отличный от null, то этот результат и будет считаться результатом проверки авторизации.

Создание политик

Генерация политик

Политики – это классы, которые организуют логику авторизации для конкретной модели или ресурса. Например, если ваше приложение является блогом, то у вас может быть модель App\Models\Post и соответствующая политика App\Policies\PostPolicy для авторизации действий пользователя, например, создание или обновление постов.

Чтобы сгенерировать новую политику, используйте команду make:policy Artisan. Эта команда поместит новый класс политики в каталог app/Policies вашего приложения. Если этот каталог не существует в вашем приложении, то Laravel предварительно создаст его:

php artisan make:policy PostPolicy

Команда make:policy сгенерирует пустой класс политики. Если вы хотите создать класс с заготовками методов политики, связанных с просмотром, созданием, обновлением и удалением ресурса, то вы можете указать параметр --model при выполнении команды:

php artisan make:policy PostPolicy --model=Post

Регистрация политик

После создания класса политики его необходимо зарегистрировать. Регистрация политик – это то, как мы можем сообщить Laravel, какую политику использовать при авторизации действий для конкретного типа модели.

Поставщик App\Providers\AuthServiceProvider вашего приложения содержит свойство $policies, которое сопоставляет ваши модели Eloquent с соответствующими политиками. Регистрация политики проинструктирует Laravel, какую политику использовать при авторизации действий для конкретной модели Eloquent:

namespace App\Providers;

use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    
    public function boot()
    {
        $this->registerPolicies();

        
    }
}

Автообнаружение политики

Вместо того чтобы вручную регистрировать политики модели, Laravel может автоматически обнаруживать политики, если модель и политика соответствуют стандартным соглашениям об именах Laravel. Так, например, модели могут быть помещены в каталог app/Models, а политики – в каталог app/Policies. Кроме того, имя политики должно совпадать с названием модели и иметь суффикс Policy. Итак, модель User будет сопоставлена классу политики UserPolicy.

Если вы хотите определить свою собственную логику обнаружения политики, то вы можете зарегистрировать замыкание обнаружения политики, используя метод Gate::guessPolicyNamesUsing. Как правило, этот метод следует вызывать из метода boot поставщика AuthServiceProvider вашего приложения:

use Illuminate\Support\Facades\Gate;

Gate::guessPolicyNamesUsing(function ($modelClass) {
    
});
Любые политики, которые явно отображены в вашем AuthServiceProvider, будут иметь приоритет над любыми потенциально автоматически обнаруженными политиками.

Написание политик

Методы политики

После регистрации класса политики вы можете добавить методы для каждого из авторизуемых действий. Например, давайте определим метод update в нашем классе PostPolicy, который решает, может ли пользователь обновить указанный экземпляр поста.

Метод update получит в качестве аргументов экземпляры User и Post и должен вернуть true или false, которые будут указывать, авторизован ли пользователь обновлять указанный пост. Итак, в этом примере мы проверим, что идентификатор пользователя совпадает с user_id поста:

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

Вы можете продолжить определение в политике необходимых методов дополнительных авторизуемых действий. Например, вы можете определить методы view или delete для авторизации различных действий, связанных с Post. Помните, что вы можете дать своим методам политики любые желаемые имена.

Если вы использовали опцию --model при создании своей политики через Artisan, то она уже будет содержать методы для следующих действий: viewAny, view, create, update, delete, restore, и forceDelete.

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

Ответы политики

До сих пор мы рассматривали методы политики, возвращающие простые логические значения. По желанию можно вернуть более подробный ответ, содержащий также сообщение об ошибке. Для этого вы можете вернуть экземпляр Illuminate\Auth\Access\Response из вашего метода политики:

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;


public function update(User $user, Post $post)
{
    return $user->id === $post->user_id
                ? Response::allow()
                : Response::deny('You do not own this post.');
}

При возврате ответа авторизации из вашей политики метод Gate::allows все равно будет возвращать простое логическое значение; однако вы можете использовать метод Gate::inspect, чтобы получить полный возвращенный шлюзом ответ авторизации:

use Illuminate\Support\Facades\Gate;

$response = Gate::inspect('update', $post);

if ($response->allowed()) {
    
} else {
    echo $response->message();
}

При использовании метода Gate::authorize, который генерирует исключение AuthorizationException при не авторизованном действие, сообщение об ошибке ответа авторизации будет передано в HTTP-ответ:

Gate::authorize('update', $post);


Методы политики без моделей

Некоторые методы политики получают только экземпляр аутентифицированного в данный момент пользователя. Эта ситуация наиболее распространена при авторизации действий create. Например, если вы создаете блог, то вы можете определить, имеет ли пользователь право вообще создавать какие-либо посты. В этих ситуациях ваш метод политики должен рассчитывать только на получение экземпляра пользователя:


public function create(User $user)
{
    return $user->role == 'writer';
}

Гостевые пользователи

По умолчанию все шлюзы и политики автоматически возвращают false, если входящий HTTP-запрос был инициирован не аутентифицированным пользователем. Однако вы можете разрешить прохождение этих проверок авторизации к вашим шлюзам и политикам, пометив в аргументе метода объявленный тип User как обнуляемый, путём добавления префикса в виде знака вопроса (?). Это означает, что значение может быть как объявленного типа User, так и быть равным null:

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    
    public function update(?User $user, Post $post)
    {
        return optional($user)->id === $post->user_id;
    }
}

Фильтры политики

Для определенных пользователей вы можете разрешить все действия в рамках конкретной политики. Для этого определите в политике метод before. Метод before будет выполнен перед любыми другими методами в политике, что даст вам возможность авторизовать действие до фактического вызова предполагаемого метода политики. Этот функционал чаще всего используется для авторизации администраторов приложения на выполнение любых действий:

use App\Models\User;


public function before(User $user, $ability)
{
    if ($user->isAdministrator()) {
        return true;
    }
}

Если вы хотите отклонить все проверки авторизации для определенного типа пользователей, вы можете вернуть false из метода before. Если возвращается null, то проверка авторизации перейдет к методу политики.

Метод before класса политики не будет вызываться, если класс не содержит метода с именем, совпадающим с именем проверяемого полномочия.