Как использовать атрибут #[Requires]
При написании веб-приложения вы часто сталкиваетесь с
необходимостью ограничить доступ к определенным частям вашего
приложения. Возможно, вы хотите, чтобы некоторые запросы могли
отправлять данные только с помощью формы (то есть методом POST), или чтобы
они были доступны только для AJAX-вызовов. В Nette Framework 3.2 появился новый
инструмент, который позволяет вам устанавливать такие ограничения
очень элегантно и наглядно: атрибут #[Requires]
.
Атрибут — это специальная метка в PHP, которую вы добавляете перед определением класса или метода. Поскольку это фактически класс, чтобы следующие примеры работали, необходимо указать клаузу use:
use Nette\Application\Attributes\Requires;
Атрибут #[Requires]
можно использовать у самого класса презентера,
а также у следующих методов:
action<Action>()
render<View>()
handle<Signal>()
createComponent<Name>()
Последние два метода относятся и к компонентам, то есть атрибут можно использовать и у них.
Если условия, указанные атрибутом, не выполнены, вызывается HTTP-ошибка 4xx.
Методы HTTP
Вы можете указать, какие HTTP-методы (например, GET, POST и т. д.) разрешены для доступа. Например, если вы хотите разрешить доступ только путем отправки формы, установите:
class AdminPresenter extends Nette\Application\UI\Presenter
{
#[Requires(methods: 'POST')]
public function actionDelete(int $id): void
{
}
}
Почему следует использовать POST вместо GET для действий, изменяющих состояние, и как это сделать? Прочитайте руководство.
Вы можете указать метод или массив методов. Особым случаем является
значение '*'
, которое разрешает все методы, что стандартно
презентеры по соображениям безопасности
не позволяют.
AJAX-вызов
Если вы хотите, чтобы презентер или метод был доступен только для AJAX-запросов, используйте:
#[Requires(ajax: true)]
class AjaxPresenter extends Nette\Application\UI\Presenter
{
}
Тот же источник
Для повышения безопасности вы можете требовать, чтобы запрос был сделан с того же домена. Это предотвратит уязвимость CSRF:
#[Requires(sameOrigin: true)]
class SecurePresenter extends Nette\Application\UI\Presenter
{
}
У методов handle<Signal>()
доступ с того же домена требуется
автоматически. Так что если, наоборот, вы хотите разрешить доступ с
любого домена, укажите:
#[Requires(sameOrigin: false)]
public function handleList(): void
{
}
Доступ через forward
Иногда полезно ограничить доступ к презентеру так, чтобы он был
доступен только косвенно, например, с использованием метода
forward()
или switch()
из другого презентера. Так, например,
защищаются error-презентеры, чтобы их нельзя было вызвать из URL:
#[Requires(forward: true)]
class ForwardedPresenter extends Nette\Application\UI\Presenter
{
}
На практике часто бывает необходимо пометить определенные представления, к которым можно получить доступ только на основе логики в презентере. То есть опять же, чтобы их нельзя было открыть напрямую:
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function actionDefault(int $id): void
{
$product = $this->facade->getProduct($id);
if (!$product) {
$this->setView('notfound');
}
}
#[Requires(forward: true)]
public function renderNotFound(): void
{
}
}
Конкретные действия
Вы также можете ограничить, чтобы определенный код, например, создание компонента, был доступен только для специфических действий в презентере:
class EditDeletePresenter extends Nette\Application\UI\Presenter
{
#[Requires(actions: ['add', 'edit'])]
public function createComponentPostForm()
{
}
}
В случае одного действия нет необходимости записывать
массив: #[Requires(actions: 'default')]
Собственные атрибуты
Если вы хотите использовать атрибут #[Requires]
повторно с теми же
настройками, вы можете создать собственный атрибут, который будет
наследовать #[Requires]
и настроит его в соответствии с
потребностями.
Например, #[SingleAction]
разрешит доступ только через действие
default
:
#[\Attribute]
class SingleAction extends Nette\Application\Attributes\Requires
{
public function __construct()
{
parent::__construct(actions: 'default');
}
}
#[SingleAction]
class SingleActionPresenter extends Nette\Application\UI\Presenter
{
}
Или #[RestMethods]
разрешит доступ через все HTTP-методы, используемые
для REST API:
#[\Attribute]
class RestMethods extends Nette\Application\Attributes\Requires
{
public function __construct()
{
parent::__construct(methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
}
}
#[RestMethods]
class ApiPresenter extends Nette\Application\UI\Presenter
{
}
Заключение
Атрибут #[Requires]
дает вам большую гибкость и контроль над тем,
как доступны ваши веб-страницы. С помощью простых, но мощных правил вы
можете повысить безопасность и правильное функционирование вашего
приложения. Как видите, использование атрибутов в Nette может не только
упростить вашу работу, но и обезопасить ее.