Как управлять авторизацией пользователя с помощью Политик (Policies). В прошлой статье мы обсудили авторизацию через Гейты. Если еще не прочли — рекомендую.
Способ управления основанный на классе — Политики
В Гейтах мы использовали способ основанный на замыканиях. Политики же больше похожи на Контроллеры. Вы можете создать Политику, используя следующую команду.
php artisan make:policy PostPolicy
Она создаст файл PostPolicy.php
в каталоге app/Policies
. Вам нужно будет добавить свои методы в этот класс. Но, если вы укажите в команде параметр --model=Post
, то она сгенерирует методы за вас. После этого нужно будет обновить в классе AuthServiceProvider
свойство $policies
, как указано ниже:
protected $policies = [ Post::class => PostPolicy::class, ];
Убедитесь, что импортируете классы правильно. Теперь давайте добавим несколько методов в наш класс Политики. Вы можете использовать следующий код.
namespace App\Policies; use App\User; use App\Post; class PostPolicy { use HandlesAuthorization; public function create (User $user) { return true; } public function edit (User $user, Post $post) { return $user->id == $post->user_id; } public function update (User $user, Post $post) { return $user->id == $post->user_id; } public function delete (User $user, Post $post) { return $user->id == $post->user_id; } }
Как и в Гейтах, методы получают текущего пользователя. Вам не нужно самостоятельно передавать его. Для проверки разрешено ли пользователю то или иное действие, мы можем использовать методы Gate::*
, описанные в предыдущей статье.
Чтобы проверить, разрешено ли пользователю редактирование сообщения, мы используем следующий способ:
Gate::allows('edit', Post::find(20));
В данном случае edit
ссылается на метод класса PostPolicy::edit
. И второй параметр приведенного выше фрагмента это второй параметр $post
метода PostPolicy::edit
. Мы можем использовать эти методы для проверки авторизации.
Всегда ли нужно регистрировать свою Политику?
Краткий ответ: нет. Laravel следует определенным соглашениям, и если вы будете также следовать им, то фреймворк сам найдет подходящий вам класс Политики.
Соглашения следующие:
- Модель (в нашем случае
Post::class
) должна находиться внутри каталогаapp
. - Политика должна находиться в каталоге
app/Policies
. - Класс политики должен быть формата
{Model}Policy
,Post
— наша модель, соответственно,PostPolicy
— название класса Политики.
Если всё так, то Model::class => ModelPolicy::class
не требует регистрации.
Предостережения, Соглашения и Иерархия
При использовании метода edit
в Gate::allow('edit', Post::find(20));
он будет называться умением (ability) и будет ссылаться на метод PostPolicy::edit
. Если название вашего метода в формате CamelCase, то название умения пишется в формате spinal-case. Это соглашение для методов Политики. Вы не можете это изменить.
Если вы не следуете соглашению Laravel для Моделей и Политик, то вам придется зарегистрировать кастомный колбэк с помощью guessPolicyNamesUsingCallback
, чтобы получить соответствующий класс Политики для класса Модели.
Больше не значит лучше. Чем больше кода выполняется, тем больше времени требуется для получения ответа. Если вы не зарегистрировали свою Политику в Модели, то Laravel придется её искать самостоятельно, а если зарегистрируете, то фреймворк сразу будет её использовать. Таким образом дольше выполняется код с незарегистрированными Политиками.
Иерархия гейтов в Laravel:
- Найти в Политике или Угадать
- Найти в Гейтах, определенных в формате
ClassName@methodName
. - Найти в Гейтах, определенных как замыкание.
Чем раньше найдет, тем раньше обслужит.
Если умение не найдено в Политиках или колбэках, то она всегда будет неавторизована.
Различные способы проверки прав
Ранее мы проверяли только с использованием методов Gate :: *
. Теперь мы разберемся с остальными.
Если вы используете класс Политики и ваш метод Политики принимает только объект $user
, то вам придется использовать его следующим образом: передайте класс Модели как строку. Передача объекта также сработает, но зачем создавать ресурс?
Gate::allows('create', Post::class); // Post::class означает, что Гейт должен указывать на PostPolicy::class
Если вы хотите проверить разрешение из маршрута, то используйте мидлвар.
Route::get('/edit/{post}', function (Post $post) { // делай что хочешь })->middleware('can:update,post'); // update — это умение // post — это модель, из параметра маршрута. // модель в мидлваре can, параметр маршрута и параметр метода должны совпадать // --------------------------- Route::get('create', function () { // делай что хочешь })->middleware('can:create,\App\Post'); // так же как и в пердыдущем фрагменте. // PostPolicy::create — не принимает никакую модель // \App\Post - определяет, что метод create находится в классе PostPolicy // Используйте для класса FQCN (Полное имя класса) // В обоих случаях если будет выброшено исключение, // то оно будет перехвачено app/Exceptions/Handler.php // и будет использован шаблон 403.blade.php.
В своих шаблонах вы можете использовать следующее
@can('update', $post) <!-- Пользователь может обновить сообщение. --> <!-- $post это модель, которая используется в PostPolicy::class --> @endcan @can('create', \App\Post::class) <!-- Пользователь может создать сообщение. --> <!-- \App\Post::class используется в PostPolicy::class --> @endcan @cannot('create', \App\Post::class) <!-- Пользователь не может создать сообщение. --> <!-- \App\Post::class используется в PostPolicy::class --> @endcannot
Вы можете напрямую применить следующие методы к модели User
User::find($id)->can('update', Post::find(20)); User::find($id)->cannot('update', Post::find(20)); User::find($id)->cant('edit', Post::find(20)); auth()->user()->can('delete', Post::find(20));
Вы можете вызвать умения из методов контроллера
$this->authorize('update', $post); // следует иерархии. В Политике или ClassName@method или Замыкании // $post это модель, либо DI из параметра маршрута // Или вы уже взяли её из базы $this->authorize('create', Post::class); // Post::class используется в PostPolicy. // если не сработало, значит отсутствует умение create // и будет выброшено исключение
Если вы используете ресурсный контроллер, то в методе __construct
вы можете использовать следующее:
namespace App\Http\Controller; class PostController extends Controller { public function __construct() { $this->authorizeResource(Post::class, 'post'); // Post::class это модель для поиска Политики // post - имя параметра } } // если вы вспомните флаг --model=Post в команде make:policy // то он в класс PostPolicy добавляет следующие методы // viewAny, view, create, update, delete, restore, forceDelete // иначе, вам придется создать их самим, чтобы вышеуказанная команда // $это->авторизацияResource() смогла правильно отработать // или перепишите метод resourceAbilityMap, чтобы он совпадал с вашими методами. // Метод PostController::index использует PostPolicy::viewAny // Метод PostController::show использует PostPolicy::view // Метод PostController::create использует PostPolicy::create // Метод PostController::store использует PostPolicy::create // Метод PostController::edit использует PostPolicy::update // Метод PostController::update использует PostPolicy::update // Метод PostController::destroy использует PostPolicy::delete // Чтобы узнать больше посмотрите файл // Illuminate/Foundation/Auth/Access/AuthorizesRequests.php:84
Это всё, что вам нужно знать о Политиках Laravel.
Автор: Syed Sirajul Islam Anik
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.