Давайте разберемся, что же такое Гейты (Gates) в Laravel и как с помощью них можно управлять авторизацией пользователя.
Авторизация и Аутентификация это разные вещи. Фасад Auth
предназначен для аутентификации. Он сверяет введенные учетные данные с сохранёнными. Если это не удается сделать, то показывается HTTP-код 401
.
Авторизация же проверяет разрешено ли вам выполнение действия. В фильмах мы часто видим, как кто-то проникает в систему, в которой он не авторизован, используя дыры в безопасности. Если вы авторизованы, то значит вам разрешено, если нет, то показывается HTTP-код 403
.
Авторизация всегда происходит после Аутентификации. Если система не может определить, кто вы, то как она сможет что-либо вам разрешать?
Как авторизоваться?
В Laravel есть два способа авторизации пользователя. Первый основан на Замыкании и называется Гейты (Gates
), а второй — на Классе и называется Политика (Policy
). В этой статье мы рассмотрим только Гейты. Они больше похожи на определение маршрута-замыкания.
Гейты — Авторизация на Замыканиях
Этот способ для авторизации использует Фасад Gate
. Фасад проксирует вызовы на Illuminate/Auth/Access/Gate.php
. Пример кода находится ниже. Вы можете добавить этот фрагмент куда угодно. Но хорошим подходом считается хранить однотипный код в одном месте. Используем для этого метод boot
в app/Providers/AuthServiceProvider.php
. Для объявления критериев авторизации мы используем метод define
. Он принимает два параметра:
name
, которое позже будет использоваться как ссылка для авторизации пользователя.- и само замыкание.
И помните, что замыкание получает залогиненного пользователя по дефолту в первом параметре.
use Illuminate\Support\Facades\Gate; Gate::define('create-post', function ($user) { return $user->id == 1; });
В этом примере мы разрешаем создавать сообщения только пользователю с id равным 1. Кроме него больше никто создавать сообщения не сможет.
Чтобы проверить, разрешено ли текущему пользователю создавать новое сообщение, нужно выполнить следующее:
Gate::allows('create-post');
Это выражение вернёт логическое значение, означающее разрешено ли пользователю это действие. Довольно просто, не так ли? Видите переменную $user
в первом параметре замыкания? По умолчанию она передается всем заданным Гейтам и Политикам и содержит текущего залогиненного пользователя.
Вы можете задать столько Гейтов, сколько вам необходимо. Приведу несколько примеров.
Gate::define('edit-post', function ($user, $post) { return $user->id == $post->user_id; }); Gate::define('delete-post', function ($user, $post) { return $user->id == $post->user_id; });
Gate::allows('delete-post', Post::find(10)); Gate::allows('edit-post', $post)
Каждый раз, когда вам нужен дополнительный параметр в вашем замыкании, вы должны его передать там, где вызываете Гейт. Дополнительные параметры можно передать двумя способами. Если вам нужен только один параметр, наряду с переменной $user
, то вы можете его передать как в примере с Gate::allows
. Но если вам нужно более одного дополнительного параметра, то необходимо использовать массив, как показано ниже.
Gate::allows('gate-name', [$param1, $param2]);
Значения массива будут переданы как параметры в ваше замыкание после переменной $user
.
Allows это единственный метод авторизации?
Нет, есть несколько методов.
allows
— проверяет, может ли быть выполнено данное действиеdenies
— проверяет, запрещено ли данное действие.check
— проверяет, разрешены ли данные действие или массив действий.any
— проверяет, разрешено ли какое-либо из указанных действийnone
— проверяет, запрещено ли какое-либо из указанных действийauthorize
— проверяет, разрешено ли действие. если нет, то выбрасывает исключениеIlluminate\Auth\Access\AuthorizationException
// use Illuminate\Auth\Access\Response Gate::allows(string $ability, $arguments = []): bool Gate::denies(string $ability, $arguments = []): bool Gate::check(array|string $abilities, $arguments = []): bool Gate::any(array|string $abilities, $arguments = []): bool Gate::none(array|string $abilities, $arguments = []): bool Gate::authorize(string $ability, $arguments = []): Response
Пользователи и Гейты
Предположим, вы хотите проверить авторизацию определенного пользователя, а не текущего. В этом случае вы можете использовать forUser($userId)
.
Gate::forUser(User::find(10))->allows('edit-post', Post::find(20));
Этот код проверит разрешено ли пользователю с ID 10
редактировать сообщение с ID 20
.
Before и After
Если вы где-то сталкиваетесь с ситуацией, что вам нужно будет подтвердить авторизацию пользователя или пользователь должен будет иметь права для совершения определенного действия, то вы можете использовать метод before
. Вы можете использовать столько колбэков before
, сколько вам нужно. Но если хотя бы один из них вернёт ненулевое значение, то оно будет возвращено как результат вашей авторизационной логики и именные Гейты использоваться далее не будет. Кроме того, вы также можете добавить множественные колбэки after
. Но если ваша кастомная авторизация или before
не вернёт никакого значения, то after
его перезапишет.
Gate::before(function ($user, $ability) { if ($user->isSuperAdmin()) { return true; } }); Gate::after(function ($user, $ability, $result, $arguments) { if ($user->isSuperAdmin()) { return true; } });
Гейты как Классы
Ранее мы определяли Гейт как:
Gate::define('ability', function (User $user){});
Но мы можем использовать классы так же, как и контроллеры в маршрутах web.php
или api.php
. Для этого мы определим Гейт следующим образом.
namespace App; class CustomPolicy { public function editPost (User $user, Post $post) { return $user->id === $post->user_id; } }
В файле app/Providers/AuthServiceProvider.php
:
namespace App\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; class AuthServiceProvider extends ServiceProvider { public function boot () { // для однострочного использования // замыкание заменяем форматом ClassName@methodName Gate::define('edit-post', 'App\CustomPolicy@editPost'); } }
Таким образом ваш провайдер остаётся чистым. А вы получаете тот же результат, что и в прошлом примере.
Вторая часть статьи об авторизации: Политики в Laravel.
Автор: Syed Sirajul Islam Anik
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.