Как использовать атрибут #[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() из другого презентера. Именно так защищаются презентеры
ошибок, чтобы их нельзя было вызвать из 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 может не только упростить вашу
работу, но и защитить её.