Як використовувати атрибут #[Requires]
Під час написання веб-додатку часто виникає потреба обмежити
доступ до певних частин вашого додатку. Можливо, ви хочете, щоб деякі
запити могли надсилати дані лише за допомогою форми (тобто методом POST),
або щоб вони були доступні лише для AJAX-викликів. У Nette Framework 3.2 з'явився
новий інструмент, який дозволяє встановити такі обмеження дуже
елегантно та зрозуміло: атрибут #[Requires]
.
Атрибут — це спеціальна позначка в PHP, яку ви додаєте перед визначенням класу або методу. Оскільки це фактично клас, щоб наступні приклади працювали, необхідно вказати оператор use:
use Nette\Application\Attributes\Requires;
Атрибут #[Requires]
можна використовувати для самого класу presenter'а,
а також для таких методів:
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 для дій, що змінюють стан, і як це зробити? Прочитайте інструкцію.
Ви можете вказати метод або масив методів. Особливим випадком є
значення '*'
, яке дозволяє всі методи, що зазвичай presenter'и з міркувань безпеки не
дозволяють.
AJAX-виклики
Якщо ви хочете, щоб presenter або метод був доступний лише для 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
Іноді корисно обмежити доступ до presenter'а так, щоб він був доступний
лише опосередковано, наприклад, за допомогою методу forward()
або
switch()
з іншого presenter'а. Таким чином, наприклад, захищаються
error-presenter'и, щоб їх не можна було викликати з URL:
#[Requires(forward: true)]
class ForwardedPresenter extends Nette\Application\UI\Presenter
{
}
На практиці часто буває необхідно позначити певні views, до яких можна отримати доступ лише на основі логіки в 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
{
}
}
Конкретні дії
Ви також можете обмежити, щоб певний код, наприклад, створення компонента, був доступний лише для специфічних дій у presenter'і:
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 може не тільки
полегшити вашу роботу, але й зробити її безпечнішою.