Jak używać atrybutu #[Requires]
Kiedy piszesz aplikację internetową, często spotykasz się z potrzebą ograniczenia dostępu do określonych
części Twojej aplikacji. Być może chcesz, aby niektóre żądania mogły wysyłać dane tylko za pomocą formularza (czyli
metodą POST), lub aby były dostępne tylko dla wywołań AJAX. W Nette Framework 3.2 pojawiło się nowe narzędzie, które
pozwoli Ci ustawić takie ograniczenia bardzo elegancko i przejrzyście: atrybut #[Requires]
.
Atrybut to specjalny znacznik w PHP, który dodajesz przed definicją klasy lub metody. Ponieważ jest to właściwie klasa, aby poniższe przykłady działały, konieczne jest podanie klauzuli use:
use Nette\Application\Attributes\Requires;
Atrybut #[Requires]
możesz użyć przy samej klasie presentera, a także przy tych metodach:
action<Action>()
render<View>()
handle<Signal>()
createComponent<Name>()
Ostatnie dwie metody dotyczą również komponentów, więc atrybut możesz używać również przy nich.
Jeśli nie są spełnione warunki, które atrybut podaje, dojdzie do wywołania błędu HTTP 4xx.
Metody HTTP
Możesz określić, które metody HTTP (jak GET, POST itp.) są dozwolone dla dostępu. Na przykład, jeśli chcesz zezwolić na dostęp tylko przez wysłanie formularza, ustawisz:
class AdminPresenter extends Nette\Application\UI\Presenter
{
#[Requires(methods: 'POST')]
public function actionDelete(int $id): void
{
}
}
Dlaczego powinieneś używać POST zamiast GET dla akcji zmieniających stan i jak to zrobić? Przeczytaj poradnik.
Możesz podać metodę lub tablicę metod. Specjalnym przypadkiem jest wartość '*'
, która zezwoli na wszystkie
metody, czego standardowo presentery z powodów bezpieczeństwa nie pozwalają.
Wywołanie AJAX
Jeśli chcesz, aby presenter lub metoda była dostępna tylko dla żądań AJAX, użyj:
#[Requires(ajax: true)]
class AjaxPresenter extends Nette\Application\UI\Presenter
{
}
To samo pochodzenie
Dla zwiększenia bezpieczeństwa możesz wymagać, aby żądanie zostało wykonane z tej samej domeny. Tym samym zapobiegniesz podatności CSRF:
#[Requires(sameOrigin: true)]
class SecurePresenter extends Nette\Application\UI\Presenter
{
}
W przypadku metod handle<Signal>()
dostęp z tej samej domeny jest wymagany automatycznie. Więc jeśli
odwrotnie chcesz zezwolić na dostęp z dowolnej domeny, podaj:
#[Requires(sameOrigin: false)]
public function handleList(): void
{
}
Dostęp przez forward
Czasami przydatne jest ograniczenie dostępu do presentera tak, aby był dostępny tylko pośrednio, na przykład używając
metody forward()
lub switch()
z innego presentera. W ten sposób na przykład chroni się
error-presentery, aby nie można było ich wywołać z URL:
#[Requires(forward: true)]
class ForwardedPresenter extends Nette\Application\UI\Presenter
{
}
W praktyce często bywa potrzeba oznaczenia określonych widoków, do których można dostać się dopiero na podstawie logiki w presenterze. Czyli ponownie, aby nie można było ich otworzyć bezpośrednio:
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
{
}
}
Konkretne akcje
Możesz również ograniczyć, że określony kod, na przykład utworzenie komponentu, będzie dostępny tylko dla specyficznych akcji w presenterze:
class EditDeletePresenter extends Nette\Application\UI\Presenter
{
#[Requires(actions: ['add', 'edit'])]
public function createComponentPostForm()
{
}
}
W przypadku jednej akcji nie trzeba zapisywać tablicy: #[Requires(actions: 'default')]
Własne atrybuty
Jeśli chcesz użyć atrybutu #[Requires]
wielokrotnie z tym samym ustawieniem, możesz stworzyć własny
atrybut, który będzie dziedziczył #[Requires]
i ustawi go według potrzeb.
Na przykład #[SingleAction]
umożliwi dostęp tylko przez akcję default
:
#[\Attribute]
class SingleAction extends Nette\Application\Attributes\Requires
{
public function __construct()
{
parent::__construct(actions: 'default');
}
}
#[SingleAction]
class SingleActionPresenter extends Nette\Application\UI\Presenter
{
}
Lub #[RestMethods]
umożliwi dostęp przez wszystkie metody HTTP używane dla 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
{
}
Zakończenie
Atrybut #[Requires]
daje Ci dużą elastyczność i kontrolę nad tym, jak dostępne są Twoje strony
internetowe. Za pomocą prostych, ale potężnych reguł możesz zwiększyć bezpieczeństwo i prawidłowe funkcjonowanie Twojej
aplikacji. Jak widzisz, użycie atrybutów w Nette może Twoją pracę nie tylko ułatwić, ale i zabezpieczyć.