Слава Україні!

Laravel. Роли и Права. Часть 3

Laravel. Роли и Права. Варианты проверки доступа
17-го липня 2023, 15:55

Частый вопрос - это как и где проверять доступ.

Вариантов весьма много. Давайте пройдёмся по ним:

Вариант 1. Маршруты: middleware('can:xxxxxx')

Непосредственно на маршруте (или группе маршрутов) можно использовать "посредника":


    Route::post('product', [ProductController::class, 'store'])
        ->middleware('can:product-manager');

Этот варианнт удобен, когда пользователю с определенным разрешением необходим доступ к нескольким действиям. Например разрешение ProductManager позволяет основные действия (CRUD) с товарами, а также дополнительно дает возможность загружать изображения и просматривать комментарии покупателей.

Вариант 2. Controller: middleware('can:xxxxxx')

Также использовать "посредника" можно непосредственно в конструкторе контроллера:


<?php
namespace App\Http\Controllers;

class ProductController extends Controller
{
    public function __construct() {
        $this->middleware('can:analog-manager');
    }
    ...
}

В этом примере будут "перекрыты" все методы контроллера.

Вариант 3. Controller: can() / cannot()

Более "точечный метод". Прописывется в первых строках метода контроллера перед основным функционалом:


public function store(Request $request)
{
    if ( !$request->user()->can('product-manager') )
        abort(403);
    }
}

Противоположный метод cannot():


public function store(Request $request)
{
    if ($request->user()->cannot('product-manager'))
        abort(403);
    }
}

Или, если у вас нет переменной $request, вы можете использовать вспомогательную функцию auth():


public function create()
{
    if (auth()->user()->cannot('product-manager'))
        abort(403);
    }
}
Вариант 4. Gate::allows() / Gate::denies()

Другой способ - использовать фасад Gate:


public function create()
{
    if (!Gate::allows('product-manager')) {
        abort(403);
    }
    ...
}

// противоположный метод:
public function store(Request $request)
{
    if (Gate::denies('product-manager')) {
        abort(403);
    }
    ...
}

// или более короткий метод с использованием вспомогательной функции:
public function store(Request $request)
{
    abort_if(Gate::denies('product-manager'), 403);
    ...
}
Вариант 5. Controller: authorize()

Ещё более короткий вариант - использовать authorize() в контроллерах. В случае неудачи он автоматически вернет страницу 403.


public function store(Request $request)
{
    $this->authorize('product-manager');
}
Вариант 6. FormRequest: authorize()

Многие разработчики генерируют классы FormRequest только для определения правил валидации, полностью игнорируя первый метод этого класса, который называется authorize().

Вы можете использовать его для проверки доступа. Таким образом, вы достигаете разделения ответственности, что является хорошей практикой. В этом случае контроллер не занимается авторизацией, потому что она выполняется в специализированном методе authorize() класса FormRequest.


public function store(StoreProductRequest $request)
{
    // в методе контроллера проверка не требуется.
}

И затем, в классе FormRequest:


class StoreProductRequest extends FormRequest
{
    public function authorize()
    {
        return Gate::allows('product-manager');
    }
    
    public function rules()
    {
        return [
            // ...
        ];
    }
}

Важлива інформація

Міністерство оборони
України
з 24.02 по 07.02
втрати противника
орієнтовно склали:
846650 ( +1340 ) особового складу
9975 ( +10 ) танків
20755 ( +18 ) бойових бронемашин
22785 ( +32 ) артилерійських систем
1271 ( +0 ) РСЗВ
1056 ( +1 ) засоби ППО
369 ( +0 ) літаків
331 ( +0 ) гелікоптерів
36307 ( +96 ) автомобільної техніки
28 ( +0 ) кораблі / катери
3054 ( +0 ) крилаті ракети
24301 ( +116 ) БПЛА
3738 ( +1 ) спец. техніки
4 ( +0 ) установок ОТРК/ТРК
1 ( +0 ) підводні човни