git e668066b85421407fc813685b483c7ab9cf49d74
- Введение
- Быстрый старт
- Ручная аутентификация пользователей
- Аутентификация HTTP Basic
- Сброс пароля
- Аутентификация через соцсети
- Создание своего гарда
- Создание своего провайдера
- События
Аутентификация - это процесс сопоставления введенных логина и пароля с данными зарегистрированных пользователей сайта и определение, является ли пользователь сайта одним из них (тогда следует логин пользователя) или нет (тогда пользователь получает сообщение об ошибке). Не путать с авторизацией - процессом проверки прав на выполнение какого-либо действия. Это разные вещи, но для каждой из них Laravel предоставляет удобные инструменты.
Аутентификация в Laravel делается очень просто. Фактически, почти всё уже готово к использованию «из коробки». Настройки аутентификации находятся в файле config/auth.php
, который содержит несколько хорошо документированных опций - посмотрите в это файл и многим из вас все сразу станет понятно.
Фактически, подсистема аутентификации Laravel состоит из двух частей:
-
Guards, "гарды", "охранники". Это по сути правила аутентификации пользователя - в каких частях запроса хранить информацию о том, что данный запрос идет от аутентифицированного пользователя. Например, это можно делать в сессии/куках, или в некотором токене, который должен содержаться в каждом запросе. В Laravel это гарды
session
иtoken
соответственно. -
Providers, "провайдеры". Они определяют, как можно получить данные пользователя из базы данных или другого места хранения. В Laravel можно получать пользователя через Eloquent и Query Builder, но вы можете написать свой провайдер, если по каким-то причинам, хотите хранить даные пользователей, например, в файле.
Можно создавать собственные гарды и провайдеры. Это нужно, если у вас, например, несколько таблиц с пользователями, или несколько областей в приложении, куда нужно логиниться отдельно, даже уже аутентифицированным пользователям - например, админка.
Но если вы только изучаете фреймворк - не беспокойтесь, чтобы использовать аутентификацию в Laravel вам не нужно досконально разбираться, как работают гарды и провайдеры. Весь необходимый код уже написан, и схема, которая принята по умолчанию, подойдет практически всем.
По умолчанию Laravel использует модель App\User
Eloquent в каталоге app
. Эта модель может использоваться вместе с драйвером аутентификации на базе Eloquent. Если ваше приложение не использует Eloquent, вы можете применить драйвер аутентификации database
, который использует Query Builder.
При создании схемы базы данных для модели App\User
убедитесь, что поле пароля имеет длину минимум в 60 символов. Дефолтное значение для поля varchar - 255 - подойдет замечательно.
Также вы должны убедиться, что ваша таблица users
(или её эквивалент) содержит строковое nullable поле remember_token
длиной в 100 символов. Это поле используется для хранения токена сессии, если ваше приложение предоставляет функциональность «запомнить меня». Создать такое поле можно с помощью $table->rememberToken();
в миграции.
Laravel оснащён двумя контроллерами аутентификации «из коробки». Они находятся в пространстве имён App\Http\Controllers\Auth
. AuthController
обрабатывает регистрацию и аутентификацию пользователей, а PasswordController
содержит логику для сброса паролей существующих пользователей. Каждый из этих контроллеров использует трейты для включения необходимых методов. В большинстве случаев вам не понадобится редактировать эти контроллеры.
Чтобы сгенерировать роуты и шаблоны, которые нужны для процесса аутентификации (страница регистрации, логина, восстановления пароля и т.п.), вам нужно даль всего одну команду:
php artisan make:auth
Вместе с ними создастся контроллер HomeController
, куда будет вести редирект после успешного логина пользователя.
Естественно, вы можете отредактировать эти сгенерированные файлы так, как вам нужно.
Как было сказано выше, шаблоны страниц регистрации создаются при помощи команды, в папке resources/views/auth
. Кроме того, будет создан главный шаблон приложения resources/views/layouts
, в который эти шаблоны будут подключаться. Вы можете строить приложение опираясь на этот главный шаблон, или использовать вместо него свой.
Контроллеры аутентификации уже были в приложении, роуты и шаблоны вы только что сгененировали. Всё, теперь пользователи могут регистрироваться и логиниться в ваше приложение.
После успешного логина пользователя надо куда-то редиректить. Куда именно - за это отвечает свойство redirectTo
класса AuthController
:
protected $redirectTo = '/home';
Если логин не успешный, то происходит автоматический редирект назад на страницу логина.
Вы также можете назначить специфичный гард для обработки процесса аутентификации. Для этого создайте свойство guard
в вашем классе AuthController
. Значением этого свойства должно быть название одного из гардов, определённых вами в файле config/auth.php
.
protected $guard = 'admin';
Для изменения полей формы, обязательных к заполнению при регистрации пользователей, или для изменения способа создания пользователей в базе данных вы можете править класс AuthController
. Этот класс отвечает за валидацию и создание новых пользователей вашего приложения.
Метод validator
класса AuthController
содержит в себе правила валидации формы регистрации новых пользователей. Вы можете изменять его, как пожелаете.
Метод create
класса AuthController
отвечает за создание новых App\User
записей в вашей базе данных, используя Eloquent ORM. Вы также можете изменять его под требования вашего приложения.
Вы можете получить аутентифицированного пользователя при помощи фасада Auth
:
$user = Auth::user();
То же самое вы можете сделать при помощи экземпляра Illuminate\Http\Request
, который инжектится в аргументы метода контроллера, обрабатывающего, например, POST-запрос:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class ProfileController extends Controller
{
/**
* Update the user's profile.
*
* @param Request $request
* @return Response
*/
public function updateProfile(Request $request)
{
if ($request->user()) {
// $request->user() возвращает экземпляр аутентифицированного пользователя
}
}
}
Для проверки текущего пользователя на аутентифицированность в вашем приложении вы можете использовать метод check
фасада Auth
, который возвращает true
, если пользователь аутентифицирован:
if (Auth::check()) {
// Пользователь аутентифицирован
}
Однако, вы также можете использовать посредников (middleware
) для проверки аутентификации пользователя перед доступом к конкретным роутам / контроллерам.
Route middleware может использоваться для разрешения доступа к роутам только аутентифицированным пользователям. В Laravel уже есть посредник auth
, который находится в файле app\Http\Middleware\Authenticate.php
. Всё, что вам нужно — указать его в описании нужного роута:
// Если роут описан как замыкание...
Route::get('profile', ['middleware' => 'auth', function() {
// Доступ разрешён только аутентифицированным пользователям...
}]);
// Или как контроллер...
Route::get('profile', [
'middleware' => 'auth',
'uses' => 'ProfileController@show'
]);
Конечно, если вы используете контроллеры, вы можете вызвать метод middleware
внутри них, в конструкторе:
public function __construct()
{
$this->middleware('auth');
}
Когда вы назначаете middleware auth
для защиты роута, вы можете явно указать, какой guard из тех, что определены у вас в config/auth.php
вы хотите использовать в данном случае:
Route::get('profile', [
'middleware' => 'auth:api',
'uses' => 'ProfileController@show'
]);
В классе AuthController
, который идет с фреймворком, есть трейт Illuminate\Foundation\Auth\ThrottlesLogins
, который реализует так называемый throttling, то есть ограничение по количеству действий за единицу времени. По умолчанию, после нескольких безуспешных попыток залогиниться, для этого IP закрывается возможность логиниться в приложение в течении минуты.
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
// Rest of AuthController class...
}
Конечно же, вы не обязаны использовать контроллеры аутентификации, включённые в Laravel. Если вы удалите эти контроллеры, нужно будет управлять аутентификацией пользователей при помощи классов аутентификации Laravel. Не волнуйтесь, это просто!
Доступ к сервисам аутентификации Laravel осуществляется при помощи фасада Auth
, поэтому нужно убедиться, что вы импортировали его перед вашим классом. Далее давайте рассмотрим метод attempt
:
<?php
namespace App\Http\Controllers;
use Auth;
use Illuminate\Routing\Controller;
class AuthController extends Controller
{
/**
* Handle an authentication attempt.
*
* @return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password])) {
// Аутентификация прошла успешно
return redirect()->intended('dashboard');
}
}
}
Метод attempt
принимает массив пар «ключ - значение» первым аргументом. Значения в этом массиве будут использованы для поиска пользователя в базе данных. Таким образом, в этом примере пользователь ищется по полю email
. Если пользователь найден, хеш пароля из базы данных сравнится с хешем указанного пароля в массиве. Если хеши совпадут, то сессия аутентификации стартует для пользователя.
Метод attempt
возвратит true
, если аутентификация прошла успешно. Иначе — false
.
Метод intended
редиректора переместит пользователя на URL, на который он хотел попасть до прохождения аутентификации. Запасной URL указывается в параметре и будет использован, если путь, куда хотел перейти пользователь, недоступен.
Если хотите, вы также можете добавить дополнительные условия прохождения аутентификации в дополнение к e-mail и паролю. Например, вы можете проверять, активен ли пользователь:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// Пользователь активен, существует и ввёл верный пароль
}
Обратите внимание: В этих примерах поле
Вы можете явно задать, при помощи какого гарда облуживать процесс авторизации. Это позволит вам иметь в приложении несколько частей, вход в которые осуществляется по своим правилам. Пользователь может быть залогинен в одну из них, или несколько. Самый простой пример - это админка. Ваш гард admin
определяет правило, залогинен данный пользователь как админ, или нет - например, установкой специальной куки.
Тогда при логине в админку вы делаете так:
if (Auth::guard('admin')->attempt($credentials)) {
//
}
Для осуществления выхода пользователя из приложения можно воспользоваться методом logout
фасада Auth
. Это очистит информацию об аутентификации из сессии пользователя:
Auth::logout();
Если вы хотите реализовать в вашем приложении функциональность «запомнить меня», можно указать булево значение вторым аргументом методу attempt
. Это аутентифицирует пользователя на неограниченное время или пока он не выйдет из приложения. Само собой, ваша таблица users
должна содержать поле remember_token
, которое будет использоваться для сохранения токена «запомнить меня».
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// Пользователь запомнен
}
Если вы «запоминаете» пользователей, можете использовать метод viaRemember
для определения того, аутентифицировался ли пользователь с помощью куки «запомнить меня»:
if (Auth::viaRemember()) {
//
}
Если вам нужно аутентифицировать существующего пользователя в ваше приложение, вы можете вызвать метод login
на экземпляре этого пользователя. Этот экземпляр должен реализовать контракт Illuminate\Contracts\Auth\Authenticatable
. Модель App\User
, используемая Laravel по умолчанию, уже реализует этот интерфейс:
Auth::login($user);
Вы также можете явно указать гард, при помощи которого будете фиксировать процесс аутентификации.
Auth::guard('admin')->login($user);
Для аутентификации пользователя по его ID вы можете использовать метод loginUsingId
. Этот метод принимает ID пользователя аргументом:
Auth::loginUsingId(1);
Вы также можете использовать метод once
для аутентификации пользователя в вашем приложении на один запрос. Ни сессии, ни куки не будут созданы, что может быть использовано для создания stateless API. Метод once
работает по тому же принципу, что и attempt
:
if (Auth::once($credentials)) {
//
}
HTTP Basic Аутентификация предоставляет быстрый способ аутентифицировать пользователя в вашем приложении без создания отдельной страницы входа. Сначала добавьте посредник auth.basic
в ваш роут. Он включён в Laravel по умолчанию, так что вам не нужно создавать его:
Route::get('profile', ['middleware' => 'auth.basic', function() {
// Только аутентифицированные пользователи могут войти
}]);
Как только посредник прикреплён к роуту, он автоматически будет показывать окно аутентификации в браузере при посещении этого роута. По умолчанию auth.basic
использует поле email
для поиска пользователя.
Если вы используете PHP FastCGI, HTTP Basic аутентификация может не работать из коробки. Вы должны добавить эти строки в ваш файл .htaccess
:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Вы также можете использовать HTTP Basic аутентификацию без установки куки идентификации пользователя в сессии, что может быть полезно при создании API аутентификации. Для этого создайте посредника, который будет вызывать метод onceBasic
. Если onceBasic
ничего не возвратит, запрос пойдёт дальше в приложение.
<?php
namespace Illuminate\Auth\Middleware;
use Auth;
use Closure;
class AuthenticateOnceWithBasicAuth implements Middleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return Auth::onceBasic() ?: $next($request);
}
}
Дальше зарегистрируйте посредника и привяжите его к роуту:
Route::get('api/user', ['middleware' => 'auth.basic.once', function() {
// Только аутентифицированный пользователь может войти
}]);
Большинство веб-приложений предоставляют пользователю возможность сбрасывать свой пароль. Для того, чтобы вам не приходилось реализовывать это в каждом приложении, Laravel предоставляет простой способ отправки «напоминателей» паролей и осуществления сброса пароля.
Для начала проверьте, реализует ли ваша модель App\User
контракт Illuminate\Contracts\Auth\CanResetPassword
. По умолчанию она это делает и использует трейт Illuminate\Auth\Passwords\CanResetPassword
, который предоставляет необходимые методы для сброса пароля.
Дальше вам необходимо создать таблицу, в которой будут храниться токены сброса паролей. Эта миграция по умолчанию уже включена в Laravel в каталог database/migrations
. Поэтому вам остаётся только мигрировать базу данных:
php artisan migrate
Laravel предоставляет класс Auth\PasswordController
, в котором содержится вся необходимая логика для сброса паролей. Для создания роутов и шаблонов вам нужно воспользоваться командой:
php artisan make:auth
Эта же команда создает в папке resources/views/auth/passwords
шаблоны для страниц восстановления пароля.
После того, как вы определили роуты и шаблоны для сброса паролей, можете проверить их в браузере, пройдя по урлу /password/reset
. Класс PasswordController
включён в фреймворк по умолчанию и содержит всю необходимую логику для отправки писем и сброса паролей в базе данных.
После сброса пароля пользователь автоматически аутентифицируется в вашем приложении и редиректится на страницу /home
. Вы можете изменить этот путь, создав свойство redirectTo
у PasswordController
protected $redirectTo = '/dashboard';
Обратите внимание: По умолчанию токен сброса пароля существует 1 час. Вы можете изменить это в конфигурационном файле
config/auth.php
с помощью опцииexpire
.
В config/auth.php
вы можете определить несколько гардов. Чтобы использовать выбранный гард для процедуры восстановления пароля, добавьте свойство в класс PasswordController
:
/**
* The authentication guard that should be used.
*
* @var string
*/
protected $guard = 'admins';
In your auth.php
configuration file, you may configure multiple password "brokers", which may be used to reset passwords on multiple user tables. You can customize the included PasswordController
to use the broker of your choice by adding a $broker
property to the controller:
/**
* The password broker that should be used.
*
* @var string
*/
protected $broker = 'admins';
You may define your own authentication guards using the extend
method on the Auth
facade. You should place this call to provider
within a service provider:
Своего гарда вы можете создать при помощи метода extend
фасада Auth
. Делать это следует в одном из ваших сервис-провайдеров, в методе boot
:
<?php
namespace App\Providers;
use Auth;
use App\Services\Auth\JwtGuard;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Auth::extend('jwt', function($app, $name, array $config) {
// Return an instance of Illuminate\Contracts\Auth\Guard...
return new JwtGuard(Auth::createUserProvider($config['provider']));
});
}
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
}
Как вы видите из примера выше, первым аргументом является имя вашего гарда, а вторым - функция, которая должна возвращать экземпляр класса, который имплементирует интерфейс Illuminate\Contracts\Auth\Guard
со всеми необходимыми для функционирования гарда методами.
Теперь вы можете использовать гард jwt
в конфигах:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
Если вы не используете традиционные реляционные базы данных для хранения пользователей, вам нужно расширить Laravel собственным провайдером аутентификации. Для этого используйте метод provider
фасада Auth
. Это код вы должны поместить в метод boot
одного из ваших сервис-провайдеров:
<?php
namespace App\Providers;
use Auth;
use App\Extensions\RiakUserProvider;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Auth::provider('riak', function($app, array $config) {
// Возвращаем класс интерфейса (контракта) Illuminate\Contracts\Auth\UserProvider...
return new RiakUserProvider($app['riak.connection']);
});
}
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
}
После добавления провайдера вы можете переключиться на него в вашем файле конфигурации config/auth.php
. Сначала поставьте его в качестве драйвера для users:
'providers' => [
'users' => [
'driver' => 'riak',
],
],
Далее мы можем юзать этот провайдер в конфигурации гардов:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
Реализации контракта Illuminate\Contracts\Auth\UserProvider
ответственны только за получение реализации контракта Illuminate\Contracts\Auth\Authenticatable
из хранилища данных, такого, например, как MySQL, Riak и тд. Эти два интерфейса позволяют Laravel использовать механизмы аутентификации независимо от того, как хранятся данные приложения.
Давайте посмотрим на контракт 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
служит для получения пользователя из базы данных по его ID. Реализация класса Authenticatable
должна быть возвращена в ответ методом.
Метод retrieveByToken
получает пользователя по его ID и токену «запомнить меня», который хранится в поле remember_token
. Как и в предыдущем методе, должна быть возвращена реализация класса Authenticatable
.
Метод updateRememberToken
обновляет токен пользователя. Новый токен может быть как снова созданным при аутентификации пользователя, так и null — при выходе пользователя.
Метод retrieveByCredentials
получает массив данных, указанных в методе Auth::attempt
, при аутентификации в приложении. Этот метод должен искать пользователя по указанным данных в хранилище данных. Обычно он ищет по полю username
. Он должен возвращать реализацию интерфейса UserInterface
. Этот метод не должен проверять пароль пользователя или аутентифицировать его.
Метод validateCredentials
должен проверять правильность данных пользователя. Например, этот метод может сравнивать строку $user->getAuthPassword()
с Hash::make
из $credentials['password']
. Этот метод должен проверять данные пользователя и возвращать булево значение.
Теперь, когда мы рассмотрели все методы UserProvider
, давайте взглянем на контракт Authenticatable
. Помните, провайдер должен возвращать реализацию этого интерфейса из методов retrieveById
и 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
должен возвращать название столбца, где хранится «primary key» пользователя, getAuthIdentifier
- собственно «primary key» пользователя. В MySQl, например, это уникальный первичный ключ. Метод getAuthPassword
должен возвращать хеш пароля пользователя. Этот интерфейс позволяет системе аутентификации работать с любым классом User независимо от используемой ORM или базы данных. По умолчанию Laravel включает в себя класс User в папке app
, который реализует этот интерфейс, поэтому вы можете использовать его, как пример.
Laravel запускает различные события в процессе процедуры аутентификации. Вы можете встроиться в процесс при помощи слушателей этих событий в вашем EventServiceProvider
:
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Auth\Events\Attempting' => [
'App\Listeners\LogAuthenticationAttempt',
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
'Illuminate\Auth\Events\Logout' => [
'App\Listeners\LogSuccessfulLogout',
],
'Illuminate\Auth\Events\Lockout' => [
'App\Listeners\LogLockout',
],
];