Авторизация
Вступление
В дополнение к аутентификации, Laravel также предоставляет простой способ авторизации действий пользователя в отношении ресурсов. Как и аутентификация, подход Ларавела к авторизации прост, и существует два основных способа авторизации действий: шлюзы и политики.
Подумайте о шлюзах и политиках, как о маршрутах и контроллерах. Шлюзы обеспечивают простой, основанный на закрытии подход к авторизации, в то время как политики, как контроллеры, группируют свою логику вокруг определенной модели или ресурса. Сначала мы рассмотрим шлюзы, а затем политики.
Вам не нужно выбирать между использованием только шлюзов или только политик. Большинство приложений, скорее всего, будут содержать смесь шлюзов и политик, и это совершенно нормально! Шлюзы наиболее применимы к действиям, которые не связаны с какой-либо моделью или ресурсом, например, просмотр панели администратора. Напротив, политики следует использовать, когда вы хотите авторизовать действие для определенной модели или ресурса.
Шлюзы (Gates)
Написание шлюзов
Шлюзы — это замыкания (closure), которые определяют, уполномочен ли пользователь выполнять заданное действие, и обычно определяются в классе App\Providers\AuthServiceProvider
с помощью фасада Gates
. Глюзы всегда получают экземпляр пользователя в качестве первого аргумента и может опционально получать дополнительные аргументы, такие как соответствующая модель Eloquent:
Шлюзы, как и контроллеры, могут быть определены с помощью строки обратного вызова в стиле Class@method
:
Авторизация действий
Чтобы авторизовать действие с использованием шлюзов, вы должны использовать методы allows
или denies
. Обратите внимание, что вы не обязаны передавать аутентифицированного пользователя этим методам. Laravel автоматически позаботится о прохождении пользователя в замыкание шлюза:
Для определения того, уполномочен ли определенный пользователь выполнять какое-либо действие, вы можете использовать метод forUser
фасада Gate
:
Вы можете санкционировать несколько действий одновременно с помощью методов any
или none
:
Авторизация или выброс исключения
Если вы хотите попытаться авторизовать действие и автоматически бросить Illuminate\Auth\Access\AuthorizationException
, если пользователю не разрешено выполнять данное действие, вы можете воспользоваться методом Gate::authorize
. Экземпляр AuthorizationException
автоматически конвертируются в HTTP ответ 403
:
Предоставление дополнительного контекста
Методы авторизации шлюза (allows
, denies
, check
, any
, none
, autorize
, can
, cannot
) и Blade-директивы авторизации (@can
, @cannot
, @canany
) могут получить массив в качестве второго аргумента. Эти элементы массива передаются в качестве параметров к шлюзу и могут быть использованы для дополнительного контекста при принятии решения о авторизации:
Ответ шлюза
До сих пор мы исследовали только те шлюзы, которые возвращают простые булевы значения. Тем не менее, иногда вы можете захотеть вернуть более подробный ответ, включая сообщение об ошибке. Для этого вы можете вернуть из шлюза Illuminate\Auth\Access\Response
:
При возврате ответа на авторизацию от шлюза, метод Gate::allow
все равно вернет простое логическое значение. Однако, вы можете использовать метод Gate::inspect
для получения полного ответа на авторизацию, возвращаемого шлюзами:
Конечно, при использовании метода Gate::authorized
выбрасывающего AuthorizationException
, если действие не авторизовано, сообщение об ошибке, предоставленное ответом авторизации, будет передано в ответ HTTP:
Перехват проверок шлюза
Иногда вы можете захотеть предоставить все возможности конкретному пользователю. Вы можете использовать метод before
для определения функции обратного вызова, которая выполняется перед всеми другими проверками авторизации:
Если функция обратного вызова в before
возвращает ненулевой результат, то этот результат будет считаться результатом проверки.
Вы можете использовать метод after
для определения функции обратного вызова, выполняемой после всех других проверок авторизации:
По аналогии с проверкой before
, если в функции обратного вызова after
возвращается ненулевой результат, то этот результат будет считаться результатом проверки.
Создание политик
Генерация политик
Политики — это классы, которые организуют логику авторизации вокруг определенной модели или ресурса. Например, если ваше приложение является блогом, у вас может быть модель Post
и соответствующая политика PostPolicy
для авторизации действий пользователя, вроде создания или обновления сообщений.
Вы можете сгенерировать политику, используя artisan-команду make:policy
. Сгенерированная политика будет помещена в каталог app/Policies
. Если этот каталог не существует в вашем приложении, Laravel создаст его:
Команда make:policy
сгенерирует пустой класс политики. Если Вы хотите сгенерировать класс с базовыми методами "CRUD", то можете указать ключ --model
при выполнении команды:
Все политики разрешаются с помощью сервис-контейнера Laravel, позволяя вам использовать тайп-хинтинг для любых необходимых зависимостей в конструкторе, чтобы они были внедрены автоматически.
Регистрация политик
Существующая политика должна быть зарегистрирована. AuthServiceProvider
в комплекте со свежими приложениями Laravel содержит свойство polisies
, которое сопоставляет ваши Eloquent-модели с их политиками. Регистрация политики инструктирует Laravel, какую политику использовать при авторизации действий данной модели:
Авто-обнаружение политик
Вместо ручной регистрации политик модели, Laravel может автоматически обнаруживать политики до тех пор, пока модель и политика следуют стандартным конвенциям об именовании. Если говорить точнее, политики должны находиться в каталоге Policies
внутри каталога, содержащего модели. Так, например, модели могут быть помещены в каталог app
, в то время как политики могут быть помещены в каталог app/Policies
. Кроме того, имя политики должно совпадать с именем модели и иметь суффикс Policy
. Таким образом, модель User
будет соответствовать классу политики UserPolicy
.
Если вы хотите предоставить свою собственную логику обнаружения политики, вы можете зарегистрировать пользовательский функцию обратного вызова, используя метод Gate::guessPolicyNamesUsing
. Обычно этот метод должен вызываться из метода boot
класса AuthServiceProvider
:
Любые политики, которые явно отображаются в вашем AuthServiceProvider
, будут иметь приоритет над любыми потенциальными автообнаруженными политиками.
Написание политик
Методы потитик
После регистрации политики вы можете добавлять методы для каждого санкционированного ею действия. Например, давайте определим метод update
в нашей PostPolicy
, который определяет, может ли данный User
обновлять данный экземпляр Post
.
Метод update
будет получать в качестве аргументов экземпляры User
и Post
, и должен возвращать true
или false
, указывая, уполномочен ли пользователь обновлять данный Post
. Итак, для данного примера, давайте проверим, совпадает ли id
пользователя с user_id
поста:
Вы можете продолжать определять дополнительные методы в политике, необходимые для различных действий, которые она разрешает. Например, Вы можете определить методы view
или delete
для авторизации различных действий Post
, но помните, что Вы можете дать своим методам политики любое имя, которое Вам понравится.
Если Вы использовали опцию --model
при генерации своей политики через консоль Artisan, то она уже будет содержать методы для действий viewAny
, view
, create
, update
, delete
, restore
и forceDelete
.
Ответ политики
До сих пор мы рассматривали только методы политики, которые возвращают простые булевые значения. Однако, иногда вы можете захотеть вернуть более подробный ответ, включая сообщение об ошибке. Для этого вы можете вернуть Illuminate\Auth\Access\Response
из метода вашей политики:
При возврате ответа на авторизацию из вашей политики, метод Gate::allow
все равно будет возвращать простое логическое значение. Однако, вы можете использовать метод Gate::inspect
для получения полного ответа на авторизацию, возвращаемого шлюзом:
Конечно, при использовании метода Gate::authorized
бросающего исключение AuthorizationException
, если действие не авторизовано, сообщение об ошибке, предоставленное ответом авторизации, будет передано в ответ HTTP:
Методы без экземпляра модели
Некоторые методы политики получают только текущего аутентифицированного пользователя, а не экземпляр модели, которую они авторизируют. Такая ситуация наиболее распространена при авторизации действий create
. Например, если вы создаете блог, вы можете проверить, авторизован ли пользователь вообще на создание каких-либо постов.
При определении методов политики, которые не получат экземпляр модели, например, метод create
, он не получит экземпляр модели. Вместо этого, вы должны определить метод как ожидающий только аутентифицированного пользователя:
Гостевые пользователи
По умолчанию, все шлюзы и политики автоматически возвращают false
, если входящий HTTP-запрос не был инициирован аутентифицированным пользователем. Однако, вы можете разрешить этим проверкам авторизации пройти к вашим шлюзам и политикам, объявив "необязательный" параметр или предоставив null
в качестве значения по умолчанию для определения аргумента пользователя:
Фильтры политики
Для некоторых пользователей вы можете разрешить все действия в рамках данной политики. Для этого определите в политике метод before
. Метод before
будет выполняться перед любыми другими методами в политике, предоставляя Вам возможность авторизовать действие до того, как будет фактически вызван предполагаемый метод политики. Эта функция наиболее часто используется для авторизации администраторов приложений на выполнение любых действий:
Если вы хотите отказать пользователю во всех авторизациях, вы должны вернуть false
из метода before
. Если возвращается null
, авторизация будет попадать под действие метода политики.
Метод before
класса политики не будет вызван, если класс не содержит метода с именем, совпадающим с именем проверяемого действия.
Авторизация действий с использованием политик
С помощью модели пользователя (User)
Модель User
, которая входит в состав вашего приложения Laravel, включает в себя два полезных метода для авторизации действий: can
и cant
. Метод can
получает действие, которое вы хотите авторизовать, и соответствующую модель. Например, давайте определим, авторизован ли пользователь на обновление заданной модели Post
:
Если для данной модели зарегистрирована политика, метод can
автоматически вызовет соответствующую политику и вернет булевый результат. Если политика для данной модели не зарегистрирована, метод can
попытается вызвать шлюз на основе Closure, соответствующий названию данного действия.
Действия, которые не требуют экземпляра модели
Помните, что некоторые действия, такие как create
, могут не требовать наличия экземпляра модели. В таких ситуациях можно передать имя класса в метод can
. Имя класса будет использоваться для определения того, какую политику использовать при авторизации действия:
С помощью посредника
Laravel включает в себя посредника, который может авторизовать действия еще до того, как входящий запрос достигнет ваших маршрутов или контроллеров. По умолчанию посреднику Illuminate\Auth\Middleware\Authorize
присвоен ключ can
в вашем классе App\Http\Kernel
. Рассмотрим пример использования посредника can
для авторизации того, что пользователь может обновлять запись в блоге:
В этом примере мы передаем два аргумента посреднику can
. Первый — это имя действия, которое мы хотим авторизовать, а второй — параметр маршрута, который мы хотим передать методу политики. В данном случае, так как мы используем неявную привязку модели, в метод политики будет передана модель Post
. Если пользователь не авторизован на выполнение данного действия, посредником будет сгенерирован HTTP-ответ с кодом состояния 403
.
Действия, которые не требуют экземпляра модели
Опять же, некоторые действия, такие как create
, могут не требовать наличия экземпляра модели. В таких ситуациях вы можете передать посреднику имя класса. Имя класса будет использоваться для определения того, какую политику использовать при авторизации действия:
С помощью помощников контроллера
В дополнение к методам, предусмотренным для модели User
, Laravel предоставляет полезный метод authorize
для любого из ваших контроллеров, который расширяет базовый класс App\Http\Controllers\Controller
. Как и метод can
, этот метод принимает имя действия, которое вы хотите авторизовать, и соответствующую модель. Если действие не авторизовано, метод authorize
бросит исключение Illuminate\Auth\Access\AuthorizationException
, которое обработчик исключений Laravel по умолчанию будет преобразовывать в HTTP-ответ с кодом состояния 403
:
Действия, которые не требуют экземпляра модели
Как уже обсуждалось ранее, некоторые действия, такие как create
, могут не требовать наличия экземпляра модели. В таких ситуациях необходимо передать методу authorize
имя класса. Имя класса будет использоваться для определения того, какую политику использовать при авторизации действия:
Авторизация контроллеров ресурсов
Если Вы используете контроллеры ресурсов, то в конструкторе контроллера можно использовать метод authorizeResource
. Этот метод прикрепит к методам контроллера ресурсов соответствующие определения посредников can
.
Метод authorizeResource
принимает в качестве первого аргумента имя класса модели, а в качестве второго аргумента — имя параметра route / request, который будет содержать идентификатор модели. Необходимо убедиться, что ваш контроллер ресурса создан с флагом --model
для того, чтобы иметь необходимые сигнатуры методов и подсказки типов:
Следующие методы контроллеров будут привязаны к их соответствующим методам политики:
Метод контроллера | Метод политики |
index | viewAny |
show | view |
create | create |
store | create |
edit | update |
update | update |
destroy | delete |
Вы можете использовать команду make:policy
с опцией --model
для быстрой генерации класса политики для данной модели: php artisan make:policy PostPolicy --model=Post
.
В шаблонах Blade
При написании Blade-шаблонов вы можете захотеть отобразить часть страницы только в том случае, если пользователь имеет право на выполнение данного действия. Например, вы можете захотеть показать форму обновления для записи в блоге только в том случае, если пользователь может действительно обновить запись. В этой ситуации Вы можете использовать директивы @can
и @cannot
:
Эти директивы являются удобными сокращениями для написания @if
и @unless
выражений. Вышеуказанные утверждения @can
и @cannot
соответствуют следующим выражениям:
Вы также можете определить, имеет ли пользователь какие-либо разрешения из заданного списка. Для этого используйте директиву @canany
:
Действия, которые не требуют экземпляра модели
Как и большинство других методов авторизации, вы можете передать имя класса в директивы @can
и @cannot
, если действие не требует экземпляра модели:
Предоставление дополнительного контекста
При авторизации действий с помощью политик вы можете передать массив в качестве второго аргумента различным функциям авторизации и помощникам. Первый элемент массива будет использоваться для определения того, какая политика должна быть вызвана, а остальные элементы массива передаются в качестве параметров методу политики и могут использоваться в дополнительном контексте при принятии решений о авторизации. Например, рассмотрим следующее определение метода PostPolicy
, который содержит дополнительный параметр $category
:
При попытке определить, может ли аутентифицированный пользователь обновить данное сообщение, мы можем использовать этот метод политики:
Last updated