Как да използвате #[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 може не само да опрости работата ви, но и да я осигури.