Авторизация: политики и Gate
Авторизация решает не «кто ты», а «что тебе можно»: политики и гейты задают правила доступа к действиям и конкретным записям.
Суть: аутентификация определяет личность, авторизация — права. Gate — простое правило-замыкание, Policy — класс с правилами для конкретной модели. Проверка идёт через
can()и директиву@can.
Войти в систему и иметь право на действие — разные вещи. Пользователь может быть авторизован (вошёл), но это не значит, что ему позволено удалять чужие статьи или открывать админ-панель. За эти правила отвечает авторизация. Laravel предлагает два инструмента: гейты для простых проверок и политики для правил, привязанных к модели.
Gate — это именованное правило в виде замыкания: «может ли этот пользователь делать X». Policy — это класс, который группирует правила вокруг одной модели: кто может смотреть, обновлять, удалять конкретную запись. Политики — основной способ авторизации в реальных проектах, потому что обычно права связаны с владением: автор может править свою статью, но не чужую.
Политика модели
php artisan make:policy PostPolicy --model=Post<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
// может ли пользователь обновить пост
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id; // только автор
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->is_admin;
}
}Проверка в контроллере и в шаблоне:
<?php
public function update(Request $request, Post $post)
{
// бросит 403, если политика запретит
$this->authorize('update', $post);
$post->update($request->validated());
return redirect()->route('posts.show', $post);
}{{-- показать кнопку только тому, кому можно --}}
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Редактировать</a>
@endcanКак работает под капотом
Когда вы вызываете $this->authorize('update', $post) или @can('update', $post), Laravel находит политику для модели Post и вызывает её одноимённый метод, передавая текущего пользователя и объект. Метод возвращает true или false. При false в контроллере выбрасывается ответ 403, а в Blade блок просто не отрисовывается. Так логика прав живёт в одном месте, а не размазана по контроллерам и шаблонам.
@can('update', $post)
|
v
PostPolicy::update($currentUser, $post)
|
user.id == post.user_id ?
| \
true false
| |
показать кнопку скрыть / 403
Смоделируем политику на Python: правило сравнивает владельца записи и текущего пользователя.
Попробуй сам ▶
# Policy: кто может редактировать пост
def can_update(user, post):
return user['id'] == post['user_id'] or user.get('is_admin')
author = {'id': 1, 'is_admin': False}
stranger = {'id': 2, 'is_admin': False}
admin = {'id': 9, 'is_admin': True}
post = {'id': 100, 'user_id': 1}
for name, u in [('автор', author), ('чужой', stranger), ('админ', admin)]:
verdict = 'можно' if can_update(u, post) else '403 запрещено'
print(f'{name:6} -> {verdict}')
Частые ошибки
- Путать с аутентификацией.
auth-middleware пускает вошедших, но не проверяет права на конкретный объект — это делают политики. - Проверять права только в Blade. Скрытая кнопка не защищает маршрут; обязательно вызывайте
authorize()и в контроллере. - Дублировать условия доступа. Если правило «только автор» повторяется в коде — его место в политике.
Best practices
- Для правил, связанных с моделью, используйте Policy; для общих — Gate.
- Проверяйте доступ и в контроллере (
authorize), и в шаблоне (@can) — защита плюс удобство. - Метод
before()в политике удобно даёт супер-админу доступ ко всему.
У политик есть несколько приёмов, без которых реальная авторизация неполна. Метод before() в классе политики выполняется раньше всех остальных и может «коротко замкнуть» проверку: если в нём вернуть true для администратора, он получит доступ ко всем действиям, минуя частные правила. Это избавляет от добавления проверки «или админ» в каждый метод. Для действий, не привязанных к конкретной записи (например, «создать пост» — записи-то ещё нет), метод политики принимает только пользователя: create(User $user). Помимо политик, есть глобальные гейты, которые регистрируют в сервис-провайдере через Gate::define('view-reports', fn($user) => $user->is_manager) — они удобны для прав, не связанных с моделью. И аутентификация, и авторизация в Laravel спроектированы так, чтобы правила доступа жили в одном месте, а контроллеры и шаблоны лишь спрашивали разрешения, не зная деталей реализации.
Итог: авторизация отвечает на вопрос «что можно», а политики и гейты держат эти правила в одном месте. Дальше соберём всё изученное в полном CRUD-приложении.