Unetway

Laravel - Authorization

Вступление

В дополнение к предоставлению услуг аутентификации из коробки, Laravel также предоставляет простой способ авторизации действий пользователя с данным ресурсом. Подобно аутентификации, подход Laravel к авторизации прост, и есть два основных способа авторизации действий: ворота и политики.

Подумайте о воротах и ​​политиках, таких как маршруты и контроллеры. Gates обеспечивает простой подход к авторизации на основе замыканий, в то время как политики, такие как контроллеры, группируют свою логику вокруг конкретной модели или ресурса. Сначала мы исследуем ворота, а затем рассмотрим правила.

Вам не нужно выбирать между исключительно использованием gates или исключительно использованием политик при создании приложения. Большинство приложений, скорее всего, будут содержать смесь ворот и политик, и это прекрасно! Ворота наиболее применимы к действиям, не связанным с какой-либо моделью или ресурсом, таким как просмотр панели администратора. Напротив, политики следует использовать, когда вы хотите авторизовать действие для конкретной модели или ресурса.

 

Gates

Написание Gates

Gates - это замыкания, которые определяют, авторизован ли пользователь для выполнения заданного действия, и обычно определяются в классе с использованием фасада. Гейтс всегда получает пользовательский экземпляр в качестве первого аргумента и может дополнительно получать дополнительные аргументы, такие как соответствующая модель Eloquent:App\Providers\AuthServiceProviderGate

/**
 * Register any authentication / authorization services.
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', function ($user, $post) {
        return $user->id == $post->user_id;
    });
}

Gates также могут быть определены с использованием Class@methodстроки обратного вызова стиля, например, контроллеров:

/**
 * Register any authentication / authorization services.
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', 'App\Policies\PostPolicy@update');
}

 

Авторизация действий

Чтобы авторизовать действие, используя ворота, вы должны использовать методы allowsили denies. Обратите внимание, что вы не обязаны передавать аутентифицированного пользователя этим методам. Laravel автоматически позаботится о пропуске пользователя в ворота. Закрытие:

if (Gate::allows('update-post', $post)) {
    // The current user can update the post...
}

if (Gate::denies('update-post', $post)) {
    // The current user can't update the post...
}

Если вы хотите определить, авторизован ли конкретный пользователь для выполнения действия, вы можете использовать forUserметод на Gateфасаде:

if (Gate::forUser($user)->allows('update-post', $post)) {
    // The user can update the post...
}

if (Gate::forUser($user)->denies('update-post', $post)) {
    // The user can't update the post...
}

 

Перехват чеков

Иногда вы можете пожелать предоставить все возможности конкретному пользователю. Вы можете использовать beforeметод для определения обратного вызова, который выполняется перед всеми другими проверками авторизации:

Gate::before(function ($user, $ability) {
    if ($user->isSuperAdmin()) {
        return true;
    }
});

Если beforeобратный вызов возвращает ненулевой результат, этот результат будет считаться результатом проверки.

Вы можете использовать afterметод для определения обратного вызова, который будет выполнен после всех других проверок авторизации:

Gate::after(function ($user, $ability, $result, $arguments) {
    if ($user->isSuperAdmin()) {
        return true;
    }
});

Аналогично beforeпроверке, если afterобратный вызов возвращает ненулевой результат, этот результат будет считаться результатом проверки.

 

Создание политик

Генерация политик

Политики - это классы, которые организуют логику авторизации вокруг конкретной модели или ресурса. Например, если ваше приложение представляет собой блог, у вас может быть Postмодель и соответствующая модель PostPolicyдля авторизации действий пользователя, таких как создание или обновление сообщений.

Вы можете создать политику, используя команду ремесленника . Сгенерированная политика будет помещена в каталог. Если этот каталог не существует в вашем приложении, Laravel создаст его для вас:make:policy app/Policies

php artisan make:policy PostPolicy

Команда сгенерирует пустой класс политики. Если вы хотите сгенерировать класс с основными методами политики «CRUD», уже включенными в класс, вы можете указать при выполнении команды:make:policy--model

php artisan make:policy PostPolicy --model=Post

Все политики разрешаются через служебный контейнер Laravel , что позволяет вам указывать любые необходимые зависимости в конструкторе политики, чтобы они автоматически вводились.

 

Регистрация политик

Как только политика существует, она должна быть зарегистрирована. В AuthServiceProviderкомплекте со свежими приложениями Laravel содержится policiesсвойство, которое сопоставляет ваши модели Eloquent с соответствующими политиками. Регистрация политики укажет Laravel, какую политику использовать при авторизации действий для данной модели:

<?php

namespace App\Providers;

use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //
    }
}

Автоопределение политики

Вместо ручной регистрации политик модели Laravel может автоматически обнаруживать политики, если модель и политика соответствуют стандартным соглашениям об именах Laravel. В частности, политики должны находиться в Policiesкаталоге под каталогом, содержащим модели. Так, например, модели могут быть размещены в appкаталоге, в то время как политики могут быть размещены в каталоге. Кроме того, имя политики должно соответствовать названию модели и иметь суффикс. Таким образом, модель будет соответствовать классу.app/PoliciesPolicyUserUserPolicy

Если вы хотите предоставить свою собственную логику обнаружения политики, вы можете зарегистрировать пользовательский обратный вызов, используя метод. Как правило, этот метод должен вызываться из метода вашего приложения :Gate::guessPolicyNamesUsingbootAuthServiceProvider

use Illuminate\Support\Facades\Gate;

Gate::guessPolicyNamesUsing(function ($modelClass) {
    // return policy class name...
});

Любые политики, которые явно указаны в вашем документе, AuthServiceProviderбудут иметь приоритет над любыми потенциальными автоматически обнаруженными политиками.

 

Написание политики

Методы политики

После регистрации политики вы можете добавить методы для каждого действия, которое она авторизует. Например, давайте определим updateметод для нашего, PostPolicyкоторый определяет, может ли данное Userлицо обновить данный Postэкземпляр.

updateМетод получит Userи в Postэкземпляре в качестве аргументов, и он должен возвращать trueили falseуказание авторизовано ли пользователь обновить данными Post. Итак, для этого примера давайте проверим, idсовпадает ли пользователь user_idс постом:

<?php

namespace App\Policies;

use App\User;
use App\Post;

class PostPolicy
{
    /**
     * Determine if the given post can be updated by the user.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return bool
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

Вы можете продолжать определять дополнительные методы политики, необходимые для различных действий, которые она разрешает. Например, вы можете определить viewили deleteметоды для авторизации различных Postдействий, но помните, что вы можете давать своим методам политики любое имя, которое вам нравится.

Если вы использовали --modelпараметр при создании вашей политики с помощью консоли Artisan, он уже будет содержать методы для viewcreateupdatedeleterestore, и forceDeleteдействия.

 

Методы без моделей

Некоторые методы политики получают только аутентифицированного пользователя, а не экземпляр модели, которую они авторизуют. Эта ситуация наиболее распространена при авторизации createдействий. Например, если вы создаете блог, вы можете проверить, имеет ли пользователь право создавать какие-либо сообщения вообще.

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

/**
 * Determine if the given user can create posts.
 *
 * @param  \App\User  $user
 * @return bool
 */
public function create(User $user)
{
    //
}

 

Гостевые пользователи

По умолчанию все шлюзы и политики автоматически возвращаются, falseесли входящий HTTP-запрос не был инициирован аутентифицированным пользователем. Однако вы можете разрешить этим проверкам авторизации проходить к вашим воротам и политикам, объявив «необязательный» тип-подсказку или предоставив nullзначение по умолчанию для определения пользовательского аргумента:

<?php

namespace App\Policies;

use App\User;
use App\Post;

class PostPolicy
{
    /**
     * Determine if the given post can be updated by the user.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return bool
     */
    public function update(?User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

 

Политические фильтры

Для определенных пользователей вы можете разрешить все действия в рамках данной политики. Для этого определите beforeметод в политике. Этот beforeметод будет выполняться перед любыми другими методами в политике, что даст вам возможность авторизовать действие до фактического вызова намеченного метода политики. Эта функция чаще всего используется для авторизации администраторов приложений для выполнения любых действий:

public function before($user, $ability)
{
    if ($user->isSuperAdmin()) {
        return true;
    }
}

Если вы хотите запретить все авторизации для пользователя, вы должны вернуться falseиз beforeметода. Если nullвозвращается, авторизация перейдет к методу политики.

beforeМетод класса политики не будет вызываться , если класс не содержит метод с именем , совпадающим с именем способности проверяется.

 

Авторизация действий с использованием политик

Через модель пользователя

UserМодель , которая поставляется вместе с приложением Laravel включает в себя две полезные методы для санкционирования действий: canа cantcanМетод получает действие , которое вы хотите разрешить и соответствующую модель. Например, давайте определим, авторизован ли пользователь для обновления данной Postмодели:

if ($user->can('update', $post)) {
    //
}

Если для данной модели зарегистрирована политика , canметод автоматически вызывает соответствующую политику и возвращает логический результат. Если для модели не зарегистрировано никакой политики, canметод попытается вызвать основанный на Closure Gate, соответствующий данному имени действия.

Действия, которые не требуют моделей

Помните, что для некоторых действий createможет не потребоваться экземпляр модели. В этих ситуациях вы можете передать имя класса canметоду. Имя класса будет использоваться, чтобы определить, какую политику использовать при авторизации действия:

use App\Post;

if ($user->can('create', Post::class)) {
    // Executes the "create" method on the relevant policy...
}

 

Через промежуточное ПО

Laravel включает промежуточное программное обеспечение, которое может авторизовать действия до того, как входящий запрос достигнет ваших маршрутов или контроллеров. По умолчанию промежуточному программному обеспечению назначается ключ в вашем классе. Давайте рассмотрим пример использования промежуточного программного обеспечения для авторизации того, что пользователь может обновлять запись в блоге:Illuminate\Auth\Middleware\AuthorizecanApp\Http\Kernelcan

use App\Post;

Route::put('/post/{post}', function (Post $post) {
    // The current user may update the post...
})->middleware('can:update,post');

В этом примере мы передаем canпромежуточному программному обеспечению два аргумента. Первый - это имя действия, которое мы хотим авторизовать, а второй - параметр маршрута, который мы хотим передать методу политики. В этом случае, поскольку мы используем неявное связывание модели , Postмодель будет передана методу политики. Если пользователь не авторизован для выполнения данного действия, 403промежуточное программное обеспечение сгенерирует HTTP-ответ с кодом состояния.

Действия, которые не требуют моделей

Опять же, некоторые действия, такие как, createмогут не требовать экземпляра модели. В этих ситуациях вы можете передать имя класса промежуточному программному обеспечению. Имя класса будет использоваться, чтобы определить, какую политику использовать при авторизации действия:

Route::post('/post', function () {
    // The current user may create posts...
})->middleware('can:create,App\Post');

 

Через помощников контроллера

В дополнение к полезным методам, предоставляемым для Userмодели, Laravel предоставляет полезный authorizeметод для любого из ваших контроллеров, расширяющих базовый класс. Как и метод, этот метод принимает имя действия, которое вы хотите авторизовать, и соответствующую модель. Если действие не авторизовано, метод выдаст значение , которое обработчик исключений Laravel по умолчанию преобразует в HTTP-ответ с кодом состояния:App\Http\Controllers\ControllercanauthorizeIlluminate\Auth\Access\AuthorizationException403

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Update the given blog post.
     *
     * @param  Request  $request
     * @param  Post  $post
     * @return Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);

        // The current user can update the blog post...
    }
}

Действия, которые не требуют моделей

Как обсуждалось ранее, некоторые действия, такие как, createмогут не требовать экземпляра модели. В этих ситуациях вы можете передать имя класса authorizeметоду. Имя класса будет использоваться, чтобы определить, какую политику использовать при авторизации действия:

/**
 * Create a new blog post.
 *
 * @param  Request  $request
 * @return Response
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function create(Request $request)
{
    $this->authorize('create', Post::class);

    // The current user can create blog posts...
}

Авторизация контроллеров ресурсов

Если вы используете контроллеры ресурсов , вы можете использовать authorizeResourceметод в конструкторе контроллера. Этот метод прикрепит соответствующее canопределение промежуточного программного обеспечения к методам контроллера ресурсов.

authorizeResourceМетод принимает имя класса модели в качестве первого аргумента, и имени маршрута / параметра запроса , который будет содержать идентификатор модели в качестве второго аргумента:

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }
}

Вы можете использовать команду с возможностью быстро создать класс политики для данной модели: .make:policy--modelphp artisan make:policy PostPolicy --model=Post

 

Шаблоны через клинок

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

@can('update', $post)
    <!-- The Current User Can Update The Post -->
@elsecan('create', App\Post::class)
    <!-- The Current User Can Create New Post -->
@endcan

@cannot('update', $post)
    <!-- The Current User Can't Update The Post -->
@elsecannot('create', App\Post::class)
    <!-- The Current User Can't Create New Post -->
@endcannot

Эти директивы являются удобными ярлыками для написания и заявлений. В и выше утверждении соответственно переводит на следующие утверждения:@if@unless@can@cannot

@if (Auth::user()->can('update', $post))
    <!-- The Current User Can Update The Post -->
@endif

@unless (Auth::user()->can('update', $post))
    <!-- The Current User Can't Update The Post -->
@endunless

Действия, которые не требуют моделей

Как и большинство других методов авторизации, вы можете передать имя класса директивам @canand, @cannotесли для действия не требуется экземпляр модели:

@can('create', App\Post::class)
    <!-- The Current User Can Create Posts -->
@endcan

@cannot('create', App\Post::class)
    <!-- The Current User Can't Create Posts -->
@endcannot