Версія фреймворка: 8.x

Авторизація

Вступ

Крім забезпеченняавтентифікаціїнестандартних служб, Laravel також пропонує простий спосіб санкціонувати дії користувачів щодо даного ресурсу. Як і автентифікація, підхід Laravel до авторизації простий, і є два основних способи санкціонування дій: Gates та політики.

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

Вам не потрібно вибирати між виключно використанням воріт або виключно використанням політик під час створення додатка. Більшість програм, швидше за все, міститимуть Mix шлюзів і правил, і це цілком нормально! Шлюзи найбільш застосовні до дій, які не пов’язані з жодною моделлю чи ресурсом, наприклад, перегляд інформаційної панелі адміністратора. На відміну від цього, політики слід використовувати, коли ви хочете дозволити дію для певної моделі чи ресурсу.

Gates

Написання Gates

Gates - це закриття, які визначають, чи має користувач дозвіл виконувати певну дію, і, як правило, визначаються вApp\Providers\AuthServiceProviderклас за допомогоюGateфасад. Гейтс завжди отримує екземпляр користувача як перший аргумент, а за бажанням може отримувати додаткові аргументи, такі як відповідна модель Eloquent:

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

    Gate::define('edit-settings', function ($user) {
        return $user->isAdmin;
    });

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

Gates також можуть бути визначені за допомогою масиву зворотного виклику класу, як контролери:

use App\Policies\PostPolicy;

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

    Gate::define('update-post', [PostPolicy::class, 'update']);
}

Авторизаційні дії

Щоб дозволити дію за допомогою воріт, вам слід використовуватиallowsабоdeniesметоди. Зверніть увагу, що вам не потрібно передавати поточно перевіреного користувача цим методам. Laravel автоматично подбає про передачу користувача в Закриття воріт:

if (Gate::allows('edit-settings')) {
    // The current user can edit settings
}

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...
}

Ви можете санкціонувати кілька дій одночасно за допомогоюanyабоnoneметоди:

if (Gate::any(['update-post', 'delete-post'], $post)) {
    // The user can update or delete the post
}

if (Gate::none(['update-post', 'delete-post'], $post)) {
    // The user cannot update or delete the post
}

Авторизація або викидання винятків

Якщо ви хочете спробувати дозволити дію та автоматично кинутиIlluminate\Auth\Access\AuthorizationExceptionякщо користувачеві не дозволено виконати дану дію, ви можете використовуватиGate::authorizeметод. ЕкземпляриAuthorizationExceptionавтоматично перетворюються на403Відповідь HTTP:

Gate::authorize('update-post', $post);

// The action is authorized...

Надання додаткового контексту

Методи шлюзу для авторизації здібностей (allows,denies,check,any,none,authorize,can,cannot) та дозвілДирективи щодо Blade(@can,@cannot,@canany) може отримати масив як другий аргумент. Ці елементи масиву передаються в якості параметрів на шлюз і можуть бути використані для додаткового контексту при прийнятті рішень щодо авторизації:

Gate::define('create-post', function ($user, $category, $extraFlag) {
    return $category->group > 3 && $extraFlag === true;
});

if (Gate::check('create-post', [$category, $extraFlag])) {
    // The user can create the post...
}

Відповіді на Gates

Наразі ми розглянули лише Gates, які повертають прості булеві значення. Однак іноді вам може знадобитися повернути більш детальну відповідь, включаючи повідомлення про помилку. Для цього ви можете повернутиIlluminate\Auth\Access\Responseвід ваших воріт:

use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function ($user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::deny('You must be a super administrator.');
});

Коли ви повертаєте відповідь авторизації з вашого входу,Gate::allowsметод все одно поверне просте логічне значення; однак ви можете використовуватиGate::inspectспосіб отримати повну відповідь авторизації, що повертається Gatesми:

$response = Gate::inspect('edit-settings', $post);

if ($response->allowed()) {
    // The action is authorized...
} else {
    echo $response->message();
}

Звичайно, при використанніGate::authorizeметод кинутиAuthorizationExceptionякщо дія не авторизована, повідомлення про помилку, надане відповіддю авторизації, буде передано до відповіді HTTP:

Gate::authorize('edit-settings', $post);

// The action is authorized...

Перехоплення перевірок воріт

Іноді ви можете надати всі можливості певному користувачеві. Ви можете використовуватиbeforeметод для визначення зворотного виклику, який запускається перед усіма іншими перевірками авторизації:

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

Якщоbeforecallback повертає ненульовий результат, який вважатиметься результатом перевірки.

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

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

Подібно доbeforeперевірити, якщоaftercallback повертає ненульовий результат, який вважатиметься результатом перевірки.

Створення політики

Створення політики

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

Ви можете створити політику, використовуючиmake:policyремісниче командування. Створена політика буде розміщена вapp/Policiesкаталог. Якщо цього каталогу немає у вашому додатку, Laravel створить його для вас:

php artisan make:policy PostPolicy

make:policyкоманда генерує порожній клас політики. Якщо ви хочете створити клас з основними методами політики "CRUD", які вже включені в клас, ви можете вказати a--modelпри виконанні команди:

php artisan make:policy PostPolicy --model=Post
Усі правила вирішуються через Laravelслужбовий контейнер, що дозволяє вам натякати на необхідні залежності в конструкторі політики, щоб їх автоматично вводити.

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

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

<?php

namespace App\Providers;

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

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. Зокрема, політика повинна відповідати aPoliciesкаталог у каталозі, що містить ваші моделі, або вище. Так, наприклад, моделі можуть бути розміщені вapp/Modelsв той час як політики можуть бути розміщені вapp/Policiesкаталог. У цій ситуації Laravel перевірить наявність політик уapp/Models/Policiesтодіapp/Policies. Крім того, назва політики повинна відповідати назві моделі та мати символPolicyсуфікс. Отже, aUserмодель відповідала б aUserPolicyклас.

Якщо ви хочете надати власну логіку виявлення політики, ви можете зареєструвати власний зворотний виклик за допомогоюGate::guessPolicyNamesUsingметод. Як правило, цей метод слід викликати зbootметод вашої програмиAuthServiceProvider:

use Illuminate\Support\Facades\Gate;

Gate::guessPolicyNamesUsing(function ($modelClass) {
    // return policy class name...
});
Будь-яка політика, яка прямо вказана у вашомуAuthServiceProviderматиме перевагу над будь-якими потенційно відкритими політиками.

Написання політики

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

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

updateметод отримає aUserі aPostекземпляр як аргументи, і повинен повернутисьtrueабоfalseвказуючи, чи уповноважений користувач оновлювати даніPost. Отже, для цього прикладу, давайте перевіримо, чи користувачidвідповідаєuser_idна посаді:

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

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

Ви можете продовжувати визначати додаткові методи в політиці, як це потрібно для різних дій, які вона санкціонує. Наприклад, ви можете визначитиviewабоdeleteметоди санкціонування різнихPostдії, але пам’ятайте, що ви можете надавати своїм методам політики будь-яке ім’я, яке вам подобається.

Якщо ви використовували--modelпри генерації вашої політики через консоль Artisan, вона вже міститиме методи дляviewAny,view,create,update,delete,restore, іforceDeleteдії.

Відповіді на політику

Наразі ми розглядали лише методи політики, які повертають прості логічні значення. Однак іноді вам може знадобитися повернути більш детальну відповідь, включаючи повідомлення про помилку. Для цього ви можете повернутиIlluminate\Auth\Access\Responseз вашого методу політики:

use Illuminate\Auth\Access\Response;

/**
 * Determine if the given post can be updated by the user.
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\Post  $post
 * @return \Illuminate\Auth\Access\Response
 */
public function update(User $user, Post $post)
{
    return $user->id === $post->user_id
                ? Response::allow()
                : Response::deny('You do not own this post.');
}

Повертаючи відповідь авторизації з вашої політики,Gate::allowsметод все одно поверне просте логічне значення; однак ви можете використовуватиGate::inspectспосіб отримати повну відповідь авторизації, що повертається Gatesми:

$response = Gate::inspect('update', $post);

if ($response->allowed()) {
    // The action is authorized...
} else {
    echo $response->message();
}

Звичайно, при використанніGate::authorizeметод кинутиAuthorizationExceptionякщо дія не авторизована, повідомлення про помилку, надане відповіддю авторизації, буде передано до відповіді HTTP:

Gate::authorize('update', $post);

// The action is authorized...

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

Деякі методи політики отримують лише поточно автентифікованого користувача, а не екземпляр авторизованої ними моделі. Така ситуація найчастіше зустрічається при наданні дозволуcreateдії. Наприклад, якщо ви створюєте щоденник, можливо, ви захочете перевірити, чи користувач взагалі уповноважений створювати будь-які дописи.

При визначенні методів політики, які не отримуватимуть екземпляр моделі, наприкладcreateметод, він не отримає екземпляр моделі. Натомість вам слід визначити метод як такий, що очікує лише автентифікованого користувача:

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

Гість користувачів

За замовчуванням усі Gates та правила автоматично повертаютьсяfalseякщо вхідний запит HTTP не був ініційований аутентифікованим користувачем. Тим не менш, ви можете дозволити цим перевіркам авторизації пройти до ваших воріт і політик, оголосивши "необов'язковий" підказку типу або надавшиnullзначення за замовчуванням для визначення аргументу користувача:

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

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

Політичні фільтри

Для певних користувачів ви можете дозволити всі дії в межах певної політики. Для цього визначте abeforeметод на полісі.beforeметод буде виконаний перед будь-якими іншими методами у політиці, що дає вам можливість санкціонувати дію до того, як запланований метод політики буде фактично викликаний. Ця функція найчастіше використовується для уповноваження адміністраторів додатків виконувати будь-які дії:

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

Якщо ви хочете заборонити всі дозволи для користувача, вам слід повернутисяfalseвідbeforeметод. Якщоnullповернеться, авторизація перейде до методу політики.

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

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

Через модель користувача

Userмодель, яка входить до програми Laravel, включає два корисні методи для санкціонування дій:canіcant.canметод отримує дію, яку ви хочете дозволити, та відповідну модель. Наприклад, давайте визначимо, чи уповноважений користувач оновлювати певнийPostмодель:

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

Якщополітика зареєстрованадля даної моделі,canметод автоматично викликає відповідну політику і повертає логічний результат. Якщо для моделі не зареєстровано жодної політики,canметод спробує викликати шлюз на основі закриття, що відповідає вказаній назві дії.

Дії, які не потребують моделей

Пам'ятайте, деякі дії на зразокcreateможе не вимагати екземпляра моделі. У цих ситуаціях ви можете передати назву класу наcanметод. Ім'я класу буде використано для визначення політики, яку слід використовувати при авторизації дії:

use App\Models\Post;

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

Через Middleware

Laravel включає Middleware, яке може санкціонувати дії, перш ніж вхідний запит потрапить навіть до ваших маршрутів або контролерів. За замовчуваннямIlluminate\Auth\Middleware\AuthorizeMiddleware присвоєноcanключ у вашомуApp\Http\Kernelклас. Давайте розберемо приклад використанняcanMiddleware, щоб дозволити користувачеві оновлювати допис у блозі:

use App\Models\Post;

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

У цьому прикладі ми передаємоcanдва аргументи проміжного програмного забезпечення. Перший - це назва дії, яку ми хочемо дозволити, а друга - параметр маршруту, який ми хочемо передати в метод політики. У цьому випадку, оскільки ми використовуємонеявна прив'язка моделі, aPostмодель буде передана методу політики. Якщо користувач не має права виконувати дану дію, відповідь HTTP із403код стану генерується проміжним програмним забезпеченням.

Дії, які не потребують моделей

Знову ж таки, такі дії, якcreateможе не вимагати екземпляра моделі. У цих ситуаціях ви можете передати назву класу проміжному програмному забезпеченню. Ім'я класу буде використано для визначення політики, яку слід використовувати при авторизації дії:

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

Через помічники контролера

На додаток до корисних методів, наданихUserмодель, Laravel надає корисну інформаціюauthorizeметод до будь-якого з ваших контролерів, які розширюютьApp\Http\Controllers\Controllerбазовий клас. Подобаєтьсяcanметод, цей метод приймає назву дії, яку ви хочете дозволити, та відповідну модель. Якщо дія не дозволена,authorizeметод кинеIlluminate\Auth\Access\AuthorizationException, який за замовчуванням обробник винятків Laravel перетворить у відповідь HTTP за допомогою403код стану:

<?php

namespace App\Http\Controllers;

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

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...
}

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

Якщо ви використовуєтеResource Контролери , ви можете скористатисяauthorizeResourceметод у конструкторі контролера. Цей метод додасть відповіднийcanвизначення проміжного програмного забезпечення до методів контролера ресурсів.

authorizeResourceметод приймає ім'я класу моделі як перший аргумент, а ім'я параметра маршруту / запиту, який буде містити ідентифікатор моделі як другий аргумент. Ви повинні забезпечити свійконтролер ресурсівстворюється за допомогою--modelпрапор, щоб мати необхідні підписи методу та підказки типу:

<?php

namespace App\Http\Controllers;

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

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

Наступні методи контролера будуть зіставлені з відповідним методом політики:

Метод контролера Метод політики
індекс viewAny
шоу вид
створити створити
магазин створити
редагувати оновлення
оновлення оновлення
знищити видалити
Ви можете використовуватиmake:policyкоманда за допомогою--modelможливість швидкого створення класу політики для даної моделі:php artisan make:policy PostPolicy --model=Post.

Через шаблони Blade

Під час написання шаблонів Blade, можливо, ви захочете відобразити частину сторінки, лише якщо користувач уповноважений виконувати певну дію. Наприклад, можливо, ви захочете показати форму оновлення для публікації в блозі, лише якщо користувач зможе насправді оновити публікацію. У цій ситуації ви можете використовувати@canі@cannotсімейство директив:

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

@cannot('update', $post)
    <!-- The Current User Cannot Update The Post -->
@elsecannot('create', App\Models\Post::class)
    <!-- The Current User Cannot Create A 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 Cannot Update The Post -->
@endunless

Ви також можете визначити, чи має користувач якісь можливості авторизації з певного переліку можливостей. Для цього скористайтеся@cananyдиректива:

@canany(['update', 'view', 'delete'], $post)
    // The current user can update, view, or delete the post
@elsecanany(['create'], \App\Models\Post::class)
    // The current user can create a post
@endcanany

Дії, які не потребують моделей

Як і більшість інших методів авторизації, ви можете передати назву класу в@canі@cannotдирективи, якщо для дії не потрібен екземпляр моделі:

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

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

Надання додаткового контексту

Під час авторизації дій із використанням політик ви можете передавати масив як другий аргумент різним функціям авторизації та помічникам. Перший елемент у масиві буде використаний для визначення, яку політику слід викликати, тоді як решта елементів масиву передаються як параметри методу політики і можуть бути використані для додаткового контексту при прийнятті рішень щодо авторизації. Наприклад, розглянемо наступнеPostPolicyвизначення методу, яке містить додатковий$categoryпараметр:

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

Під час спроби визначити, чи може автентифікований користувач оновити дану публікацію, ми можемо застосувати цей метод політики так:

/**
 * 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, $request->input('category')]);

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