Аутентификация

Содержание:


Введение

Хотите быстрый старт? Просто выполните команду php artisan make:auth и php artisan migrate для нового приложения Ларавел. После чего откройте адрес http://your-app.test/register в браузере или другой маршрут вашего приложения. Эти две команды позаботятся о реализации системы аутентификации для вашего приложения.

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

Возможности аутентификации Ларавел делятся на "защитников" и "провайдеров". Защитники определяют то, как пользователи были аутентифицированы для каждого запроса. Например, Laravel защитник session, который поддерживает состояние используя хранилище сессий и cookies.

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

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

Решения по базе данных

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

При построении базы данных для модели App\User, убедитесь в том, что колонка пароля длиной в 60 символов как минимум. Хорошим выбором будет указать значение длины колонки в 255 символов.

Также вы должны провести верификацию того, что ваша таблица users (или эквивалент) содержит nullable строку remember_token из 100 символов. Эта колонка будет использоваться для хранения токена для пользователей, которые выбрали пункт "запомнить меня" при входе в приложение.


Быстрый старт

Laravel поставляется с несколькими встроенными контроллерами аутентификации, которые расположены в App\Http\Controllers\Auth. RegisterController обрабатывает регистрацию новых пользователей, LoginController работает с аутентификацией, ForgotPasswordController высылает ссылки электронной почты для того, чтобы задать новый пароль, и ResetPasswordController содержит логику присвоения новых паролей. Каждый из этих контроллеров используют трейты для включения необходимых методов. Для большгого количества приложений вам не потребуется модифицировать эти контроллеры вовсе.

Маршрутизация

Ларавел предоставляет быстрый способ настроить маршруты, файлы и всё что нужно для аутентификации используя одну простую команду:

php artisan make:auth

Эту команду нужно использовать для новых установок Ларавел. После её выполнения, вы получите представления регистрации и для логина, а также маршруты для всех конечных точен аутентификации. Будет также сгенерирван HomeController для обработки запросов логина в панель управления вашего приложения.

Если ваше приложение не нуждается в регистрации, вы можете отключить путём удаления только что созданного RegisterController, а также изменить декларацию маршрута: Auth::routes(['register' => false]);.

Представления

Как и упоминалось в предыдущей секции, команда php artisan make:auth создаст все необходимы для аутентификации представления и разместит их в папке resources/views/auth.

Команда make:auth также создаст папку resources/views/layouts, которая содержит базовый шаблон для вашего приложения. Все эти представления используют CSS фреймворк Bootstrap, конечно вы можете легко заменить его на любой другой или собственный.

Аутентификация

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

Настройка путей

После успешной аутентификации пользователя, он может быть перенаправлен на страницу /home. Вы можете настроить перенаправление путём определения свойства redirectTo в контроллерах: LoginController, RegisterController, ResetPasswordController, и VerificationController:

protected $redirectTo = '/';

После чего, вы должны модифицировать метод RedirectIfAuthenticated для посредника handle, чтобы использовать новый URI при перенаправлении пользователя.

Если вам необходимо внедрить некоторую логику для перенаправления, тогда вы можете использовать метод redirectTo вместо свойства redirectTo:

protected function redirectTo()
{
    return '/path';
}
Метод redirectTo имеет приоритет над атрибутом redirectTo.

Настройка имени пользователя

По умолчанию, Ларавел использует поле email для аутентификации. Если вы хотите изменить это, вы можете определить метод username для вашего LoginController:

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

Настройка защитника

Вы также можете настроить защитника, который используется для аутентификации и регистрации пользователей. Для начала, определите метод guard для ваших LoginController, RegisterController, и ResetPasswordController. Этот метод должен вернуть экземпляр стража:

use Illuminate\Support\Facades\Auth;

protected function guard()
{
    return Auth::guard('guard-name');
}

Валидация / Настройка хранилища

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

Метод validator контроллера RegisterController содержит правила валидации для новых пользователей приложения. Вы можете модифицировать этот метод как захотите.

Метод create для контроллера RegisterController отвечает за создание новой записи App\User в вашей базе данных используя Eloquent. Вы можете изменить этот метод как считаете необходимым.

Получение пользователей, которые прошли аутинтификацию

Вы можете получить доступ к аутентифицированному пользователю через фасад Auth:

use Illuminate\Support\Facades\Auth;

// Get the currently authenticated user...
$user = Auth::user();

// Get the currently authenticated user's ID...
$id = Auth::id();

Как вариант, вы можете получить доступ к пользователю через экземпляр Illuminate\Http\Request. Как обычно. вписанные классы будут автоматически внедрены в методы контроллера:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProfileController extends Controller
{
    /**
     * Update the user's profile.
     *
     * @param  Request  $request
     * @return Response
     */
    public function update(Request $request)
    {
        // $request->user() returns an instance of the authenticated user...
    }
}

Определение того, что текущий пользователь аутентифицирован

Чтобы определить, что текущий пользователь уже вошел в ваше приложение, вы можете использовать метод check для фасада Auth, который вернёт true, если пользователь аутентифицирован:

use Illuminate\Support\Facades\Auth;

if (Auth::check()) {
    // The user is logged in...
}
Хоть есть такая возможность опрелить, что пользователь вошел через метод check, обычно вы будете использовать посредника для верификации аутентификации пользователя до момента доступа к определённым маршрутам и контроллерам. Больше информации можете узнать из следующего раздела.

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

Можно использовать посредник для того, чтобы только аутентифицированным пользователям позволить получить доступ к представленному маршруту. Ларавел поставляется с включённым посредником auth, который определён в Illuminate\Auth\Middleware\Authenticate. В силу того, что данный посредник уже зарегистрирован, всё что вам необходимо — это прикрепить посредника к определению маршрута:

Route::get('profile', function () {
    // Only authenticated users may enter...
})->middleware('auth');

Когда вы используете контроллер, вы можете вызвать метод middleware из конструктора контроллера, вместо прикрепления его к определению маршрута напрямую:

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

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

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

/**
 * Get the path the user should be redirected to.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return string
 */
protected function redirectTo($request)
{
    return route('login');
}

Указание стража

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

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

Регулириование входа

Если вы используете встроенный в Ларавел класс LoginController, трейт Illuminate\Foundation\Auth\ThrottlesLogins будет уже включён в контроллер. По умолчанию, пользователь не сможет залогиниться в течение минуты, если не предоставили верных учётных данных после несокольких попыток. Блокировка привязана к имени пользователя, e-mail адресу или IP адресу.


Проверка подлинности пользователй вручную

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

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

<?php

namespace App\Http\Controllers;

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

class LoginController extends Controller
{
    /**
     * Handle an authentication attempt.
     *
     * @param  \Illuminate\Http\Request $request
     *
     * @return Response
     */
    public function authenticate(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            // Authentication passed...
            return redirect()->intended('dashboard');
        }
    }
}

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

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

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

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

Если вы хотите, вы можете добавить дополнительный условия для запроса аутентификации в дполнение к email и паролю пользователя. Например, вы можете верефицировать, что пользоватлеь отмечен как "активный":

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
    // The user is active, not suspended, and exists.
}
В этих примерах, email это необязательная опция, использовалась просто как пример. Вы должны использовать такое имя колонки, которое соответсвует "username" в базе данных.

Доступ к конкретным экземплярам стража

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

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

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

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

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

Auth::logout();

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

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

if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
    // The user is being remembered...
}
Если вы используете встроенный контроллер LoginController, необходимая логика для "запоминания" уже реализована путём использования трейтов контроллером.

Если вы запоминаете пользователей, вы можете использовать метод viaRemember, чтобы определить, что пользователь аутентифицирован используя "запомнить меня" cookie:

if (Auth::viaRemember()) {
    //
}

Другие методы

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

Если необходимо залогинить экземпляр существующего пользователя в приложение, вы можете вызвать метод login с экземпляром пользователя. Данный объект должен быть реализацией контракта Illuminate\Contracts\Auth\Authenticatable. Включённая в Ларавел модель уже расширяет этот интерфейс:

Auth::login($user);

// Login and "remember" the given user...
Auth::login($user, true);

Вы можете указать стража, которого хотите использовать:

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

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

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

Auth::loginUsingId(1);

// Login and "remember" the given user...
Auth::loginUsingId(1, true);

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

Вы может использовать метод once, чтобы залогинить пользователя в приложении для одного запроса. В этом случае не используются сессии или cookies. Фактически, этот метод может быть полезен для API:

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

Базовая аутинтификация HTTP

Базовая HTTP аутентификация предоставляет быстрый способ аутентификации пользователей вашего приложения без создания дополнительной страницы "логин". Для начала, прикрепите посредника auth.basic к вашему маршруту. Его не нужно определять, из-за того, что он влючён в Ларавел:

Route::get('profile', function () {
    // Only authenticated users may enter...
})->middleware('auth.basic');

После того, как посредник был прикреплён к маршруту, вы автоматически получите предложение ввести учётные данные при обращении к маршруту в вашем браузере. По умолчанию, посредник auth.basic будет использовать колонку email из записи пользователя в качестве "username".

Заметка о FastCGI

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

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

Базовая аутинтификация HTTP без прав

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

<?php

namespace App\Http\Middleware;

use Illuminate\Support\Facades\Auth;

class AuthenticateOnceWithBasicAuth
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, $next)
    {
        return Auth::onceBasic() ?: $next($request);
    }

}

Следующим шагом зарегистрируйте посредник и прикрепите его к маршруту:

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

Выход

Чтобы вручную вывести пользователия из приложения, вы можете использовато метод logout для фасада Auth. Такое действие удалит всю информацию о сессии пользователя:

use Illuminate\Support\Facades\Auth;

Auth::logout();

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

Ларавел также обеспечивает механизм для анулирования пользовательских сессий, которые активны на других устройствах, без анулирования сессии для текущего устройства. Перед началом, необходимо убедиться в том, что посрденик Illuminate\Session\Middleware\AuthenticateSession присутствует и не закоментирован в классе app/Http/Kernel.php группе посредников web:

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

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

use Illuminate\Support\Facades\Auth;

Auth::logoutOtherDevices($password);
При вызове метода logoutOtherDevices все остальные сессии пользователя будут анулированы, что означает, пользователь будет выведен из всех стражей, в которых ранее он был аутентифицирован.

Добавление пользовательских защитников

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

<?php

namespace App\Providers;

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

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

        Auth::extend('jwt', function ($app, $name, array $config) {
            // Return an instance of Illuminate\Contracts\Auth\Guard...

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

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

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

Замыкание запросов защитников

Самый простой способ реализовать пользовательскую, основанную на HTTP, систему аутентификации — это использовать метод Auth::viaRequest. Он позволит вам быстро определить ваш процесс аутентификации, используя Замыкание.

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

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

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

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

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

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

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

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

<?php

namespace App\Providers;

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

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

        Auth::provider('riak', function ($app, array $config) {
            // Return an instance of Illuminate\Contracts\Auth\UserProvider...

            return new RiakUserProvider($app->make('riak.connection'));
        });
    }
}

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

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

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

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

Контракт User провайдера

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

Давайте взглянем на контракт Illuminate\Contracts\Auth\UserProvider:

<?php

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 соединяет ID, который должен быть получен и возвращен методом.

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

Метод updateRememberToken обновляет $user поле remember_token новым $token. Новый токен приписывает для успешного входа с пометко "запомнить меня", или когда пользователь выходит из системы.

Метод retrieveByCredentials получает массив данных передаваемых в метод Auth::attempt при попытке входа в прлиожение. Метод должен сделать запрос в постоянное хранилище для того, чтобы найти соответствующую запись пользователя с этими данными. Обычно, метод выполнит запрос с условием "where" для $credentials['username']. После чего метод должен вернуть реализацию Authenticatable. Этот метод не должен пытаться делать любую валидацию пароля или аутентификации.

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

Контракт Authenticatable

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

<?php

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 должен вернуть захешированный пароль пользователя. Этот интерфейс позволяет системе аутентификации работать с любым классом пользователя, вне зависимости от того, какую ORM или какой слой работы с бд вы используете. По умолчанию, Ларавел включает класс User в папке app, который реализует этот интерфейс, поэтому вы можете обратиться к этому класса для примера реализации.


События

В процессе аутентификации Ларавел абращается к большоми числу событий. Вы можете прикрепить слушателя к этим событиям в вашем EventServiceProvider:

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    'Illuminate\Auth\Events\Registered' => [
        'App\Listeners\LogRegisteredUser',
    ],

    'Illuminate\Auth\Events\Attempting' => [
        'App\Listeners\LogAuthenticationAttempt',
    ],

    'Illuminate\Auth\Events\Authenticated' => [
        'App\Listeners\LogAuthenticated',
    ],

    'Illuminate\Auth\Events\Login' => [
        'App\Listeners\LogSuccessfulLogin',
    ],

    'Illuminate\Auth\Events\Failed' => [
        'App\Listeners\LogFailedLogin',
    ],

    'Illuminate\Auth\Events\Logout' => [
        'App\Listeners\LogSuccessfulLogout',
    ],

    'Illuminate\Auth\Events\Lockout' => [
        'App\Listeners\LogLockout',
    ],

    'Illuminate\Auth\Events\PasswordReset' => [
        'App\Listeners\LogPasswordReset',
    ],
];