Авторизация: Gates и Policies

Рейтинг: 64.6% · 12 голосов
Курс по Laravel: маршруты, Eloquent, Blade, миграции, очереди и API. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
oleg_php
Сообщения: 25
Зарегистрирован: 14 май 2026, 08:06

Авторизация: Gates и Policies

Сообщение oleg_php »

АкадемияLaravel с нуляГлава 13 из 18
Оглавление курса (18)
  1. Знакомство с Laravel и установка окружения
  2. Маршруты и контроллеры
  3. Blade: шаблоны и вёрстка страниц
  4. Миграции и структура базы данных
  5. Eloquent ORM: модели и CRUD
  6. Связи в Eloquent: hasMany, belongsTo и другие
  7. Формы и валидация данных
  8. Аутентификация пользователей
  9. Middleware и защита маршрутов
  10. Очереди и фоновые задачи
  11. Отправка почты и уведомления
  12. Строим REST API на Laravel
  13. Авторизация: Gates и Policies (вы здесь)
  14. Работа с файлами: загрузка, Storage, диски local и S3
  15. Тестирование: Pest и PHPUnit, фабрики, сидеры, RefreshDatabase
  16. Сервис-контейнер, провайдеры, свои artisan-команды и планировщик
  17. Деплой в продакшен и обзор современного фронтенда (Vite, Livewire, Inertia)
  18. События и слушатели, кеширование, логирование
В главе 8 приложение научилось узнавать пользователя, в главе 9 мы закрыли маршруты через middleware auth. Но auth отвечает только на вопрос "вошел ли ты", а не "можно ли тебе это действие". Залогиненный пользователь не должен редактировать чужие посты, и за это отвечает уже авторизация. В Laravel для нее два инструмента: Gates для одиночных действий и Policies для действий над моделями.

Когда хватает Gate:

Gate, по сути, именованное замыкание. Определяется в boot() у AppServiceProvider:

Код: Выделить всё

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

public function boot(): void
{
    Gate::define('access-admin-panel', function (User $user) {
        return $user->role === 'admin';
    });
}
Проверять можно где угодно:

Код: Выделить всё

if (Gate::allows('access-admin-panel')) {
    // пускаем
}

Gate::authorize('access-admin-panel'); // бросит 403, если нельзя
Gates хороши для действий без привязки к конкретной записи: доступ к админке, экспорт отчетов, переключение фич. Как только появляется набор проверок вокруг одной модели, переходите на Policy, иначе AppServiceProvider распухнет.

Policy для модели:

Код: Выделить всё

php artisan make:policy PostPolicy --model=Post
Команда создаст app/Policies/PostPolicy.php с заготовками viewAny, view, create, update, delete, restore и forceDelete. Регистрировать ничего не нужно: для App\Models\Post фреймворк сам найдет App\Policies\PostPolicy по соглашению об именах. Ручная привязка через Gate::policy() нужна только при нестандартных неймспейсах.

Код: Выделить всё

namespace App\Policies;

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

class PostPolicy
{
    public function before(User $user, string $ability): ?bool
    {
        return $user->isAdmin() ? true : null;
    }

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

    public function delete(User $user, Post $post): Response
    {
        return $user->id === $post->user_id
            ? Response::allow()
            : Response::deny('Удалять можно только свои посты.');
    }
}
before() выполняется до любого метода. Вернули true, доступ открыт. Вернули null, проверка идет дальше по обычным правилам. Методы могут возвращать не только bool: Response::deny() отдает осмысленный текст вместо голого 403, а Response::denyAsNotFound() вернет 404, когда сам факт существования записи нужно скрыть (черновики, приватные разделы).

Где ставить проверки:

Начиная с Laravel 11 базовый контроллер пустой, метода $this->authorize() в нем из коробки нет (трейт AuthorizesRequests подключается вручную, если хочется). Проще использовать фасад Gate:

Код: Выделить всё

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

public function update(Request $request, Post $post)
{
    Gate::authorize('update', $post);
    // валидация и сохранение
}

public function store(Request $request)
{
    Gate::authorize('create', Post::class); // объекта еще нет, передаем класс
}
Та же проверка доступна в Blade и прямо на маршруте:

Код: Выделить всё

@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Редактировать</a>
@endcan

Код: Выделить всё

Route::delete('/posts/{post}', [PostController::class, 'destroy'])
    ->middleware('auth')
    ->can('delete', 'post');
В Form Request из главы 7 проверку кладут в метод authorize(): return $this->user()->can('update', $this->route('post'));

Типичные грабли:

Первое и главное: @can в шаблоне прячет кнопку, но не защищает действие. Запрос можно отправить через curl мимо вашей верстки, поэтому проверка обязана стоять в контроллере или на маршруте, а Blade только косметика.

before() с return false вместо return null. false мгновенно запрещает все и всем, и вы будете долго искать, почему владелец не может править собственный пост.

Гости. Для неавторизованного пользователя policy даже не вызывается, Laravel сразу возвращает отказ. Если страница должна быть видна гостям, объявите параметр nullable: public function view(?User $user, Post $post).

Путаница с create. Записи еще нет, поэтому передается имя класса, Post::class. Передадите класс там, где ожидается инстанс, получите ложный отказ и неочевидный дебаг.

И не дублируйте логику. Если правило "владелец или админ" уже в policy, не повторяйте его в Blade условием $post->user_id === auth()->id(). Правило поменяется, а один из трех экземпляров вы забудете.

Итог:

Gates для разовых действий, Policies для CRUD по моделям, before() для суперадмина, Response для внятных отказов. Проверки живут на сервере, а не в шаблоне. В следующей главе займемся файлами: загрузка, Storage, диски local и S3. Авторизация там тоже пригодится, скачивать чужие файлы нельзя точно так же, как редактировать чужие посты.
👍5 ❤️2 🔥2 😄 🤔1
✔ Лучший ответ сформирован автоматически — soy1028
oleg_php писал(а):before() с return false вместо return null. false мгновенно запрещает все и всем, и вы будете долго искать, почему владелец не может править собственный пост. плавали. у нас в before вернули false для забаненных и положили заодно всю модерацию, тесты были только на счастливые сценарии. два часа дебага на проде, больше так не делаем
Перейти к ответу →
Аватара пользователя
styontp2
Сообщения: 1
Зарегистрирован: 14 май 2026, 08:12

Re: Авторизация: Gates и Policies

Сообщение styontp2 »

наконец-то дошло чем gate отличается от policy, а то в доке это как-то размазано. вопрос: если ролей всего три (админ, модер, юзер), есть смысл тащить spatie/laravel-permission или хватит policy с before()? не хочется ставить пакет ради трех if-ов
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
soy1028
Сообщения: 1
Зарегистрирован: 22 май 2026, 18:26

Re: Авторизация: Gates и Policies

Сообщение soy1028 »

✔ Лучший ответ — сформирован автоматически
oleg_php писал(а):before() с return false вместо return null. false мгновенно запрещает все и всем, и вы будете долго искать, почему владелец не может править собственный пост.
плавали. у нас в before вернули false для забаненных и положили заодно всю модерацию, тесты были только на счастливые сценарии. два часа дебага на проде, больше так не делаем
👍1 ❤️ 🔥 😄 🤔
Аватара пользователя
wireguardmaster
Сообщения: 3
Зарегистрирован: 03 июн 2026, 23:49

Re: Авторизация: Gates и Policies

Сообщение wireguardmaster »

про denyAsNotFound не знал, всегда руками кидал abort(404) после проверки в контроллере. для черновиков реально аккуратнее. и спасибо за момент с ?User, у меня публичная страница поста как раз отдавала 403 гостям и я грешил на middleware
👍2 ❤️2 🔥 😄 🤔
Аватара пользователя
hippies
Сообщения: 2
Зарегистрирован: 23 май 2026, 02:38

Re: Авторизация: Gates и Policies

Сообщение hippies »

а ->can() на маршруте и Gate::authorize в контроллере одновременно ставить, это перебор или норм? по идее двойная проверка лишняя, но как-то спокойнее когда защита не в одном месте
👍2 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Строим REST API на Laravel
Следующая глава →
Работа с файлами: загрузка, Storage, диски local и S3

Все главы курса «Laravel с нуля»

Поделиться темой: ✈ Telegram VK

Вернуться в «Laravel с нуля»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость