Nette Documentation Preview

syntax
Authentikáció
*************

A Nette módot biztosít az authentikáció programozására az oldalainkon, de semmire sem kényszerít minket. Az implementáció kizárólag rajtunk múlik. A Nette tartalmazza a `Nette\Security\Authenticator` interfészt, amely csak egyetlen `authenticate` metódust követel meg, amely a felhasználót bármilyen általunk kívánt módon ellenőrzi.

Számos lehetőség van arra, hogyan lehet egy felhasználót hitelesíteni. A leggyakoribb hitelesítési mód a jelszó használata (a felhasználó megadja a nevét vagy e-mail címét és jelszavát), de vannak más módszerek is. Talán ismeri a "Bejelentkezés Facebookkal" típusú gombokat, vagy a Google/Twitter/GitHub segítségével történő bejelentkezést néhány oldalon. A Nette segítségével bármilyen bejelentkezési módszerünk lehet, vagy akár kombinálhatjuk is őket. Ez csak rajtunk múlik.

Általában saját authentikátort írnánk, de ehhez az egyszerű kis bloghoz a beépített authentikátort fogjuk használni, amely a konfigurációs fájlban tárolt jelszó és felhasználónév alapján jelentkeztet be. Tesztelési célokra jól használható. Adjunk hozzá tehát a következő `security` szekciót a `config/common.neon` konfigurációs fájlhoz:


```neon .{file:config/common.neon}
security:
	users:
		admin: secret  # felhasználó 'admin', jelszó 'secret'
```

A Nette automatikusan létrehoz egy szolgáltatást a DI konténerben.


Bejelentkezési űrlap
====================

Most már készen áll az authentikáció, és elő kell készítenünk a felhasználói felületet a bejelentkezéshez. Hozzunk létre tehát egy új presentert `SignPresenter` néven, amely:

- megjeleníti a bejelentkezési űrlapot (felhasználónévvel és jelszóval)
- az űrlap elküldése után ellenőrzi a felhasználót
- lehetőséget biztosít a kijelentkezésre

Kezdjük a bejelentkezési űrlappal. Már tudjuk, hogyan működnek az űrlapok a presenterekben. Hozzunk létre tehát egy `SignPresenter` presentert, és írjuk meg a `createComponentSignInForm` metódust. Valahogy így kellene kinéznie:

```php .{file:app/Presentation/Sign/SignPresenter.php}
<?php
namespace App\Presentation\Sign;

use Nette;
use Nette\Application\UI\Form;

final class SignPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentSignInForm(): Form
	{
		$form = new Form;
		$form->addText('username', 'Felhasználónév:')
			->setRequired('Kérjük, töltse ki a felhasználónevét.');

		$form->addPassword('password', 'Jelszó:')
			->setRequired('Kérjük, töltse ki a jelszavát.');

		$form->addSubmit('send', 'Bejelentkezés');

		$form->onSuccess[] = $this->signInFormSucceeded(...);
		return $form;
	}
}
```

Vannak mezők a felhasználónévhez és a jelszóhoz.


Sablon
------

Az űrlap az `in.latte` sablonban fog renderelődni:

```latte .{file:app/Presentation/Sign/in.latte}
{block content}
<h1 n:block=title>Bejelentkezés</h1>

{control signInForm}
```


Bejelentkezési callback
-----------------------

Ezután hozzáadjuk a felhasználó bejelentkezéséhez szükséges callbacket, amely az űrlap sikeres elküldése után azonnal meghívódik.

A callback csak átveszi a felhasználó által kitöltött felhasználónevet és jelszót, és átadja azokat az authentikátornak. Bejelentkezés után átirányítunk a kezdőoldalra.

```php .{file:app/Presentation/Sign/SignPresenter.php}
private function signInFormSucceeded(Form $form, \stdClass $data): void
{
	try {
		$this->getUser()->login($data->username, $data->password);
		$this->redirect('Home:');

	} catch (Nette\Security\AuthenticationException $e) {
		$form->addError('Helytelen felhasználónév vagy jelszó.');
	}
}
```

A [User::login() |api:Nette\Security\User::login()] metódus kivételt dob, ha a felhasználónév és a jelszó nem egyezik a konfigurációs fájlban szereplő adatokkal. Ahogy már tudjuk, ez piros hibaoldalt eredményezhet, vagy éles módban egy szerverhibáról tájékoztató üzenetet. Ezt azonban nem akarjuk. Ezért elkapjuk ezt a kivételt, és egy szép, felhasználóbarát hibaüzenetet adunk át az űrlapnak.

Amint hiba történik az űrlapon, az űrlapot tartalmazó oldal újrarajzolódik, és az űrlap felett megjelenik egy szép üzenet, amely tájékoztatja a felhasználót, hogy rossz felhasználónevet vagy jelszót adott meg.


Presenterek védelme
===================

Biztosítsuk a bejegyzések hozzáadására és szerkesztésére szolgáló űrlapot. Ez az `EditPresenter` presenterben van definiálva. A cél az, hogy megakadályozzuk a nem bejelentkezett felhasználók hozzáférését az oldalhoz.

Létrehozunk egy `startup()` metódust, amely azonnal elindul a [presenter életciklusának |application:presenters#Presenter életciklusa] elején. Ez átirányítja a nem bejelentkezett felhasználókat a bejelentkezési űrlapra.

```php .{file:app/Presentation/Edit/EditPresenter.php}
public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}
```


Linkek elrejtése
----------------

Az illetéktelen felhasználó már nem láthatja a *create* vagy *edit* oldalt, de továbbra is láthatja a rájuk mutató linkeket. Ezeket is el kellene rejtenünk. Egy ilyen link az `app/Presentation/Home/default.latte` sablonban található, és csak a bejelentkezett felhasználók láthatják.

Elrejthetjük egy *n:attribútum* használatával, amelynek neve `n:if`. Ha ez a feltétel `false`, az egész `<a>` tag, beleértve a tartalmát is, rejtve marad.

```latte
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Bejegyzés létrehozása</a>
```

ami a következő jelölés rövidítése (nem összetévesztendő a `tag-if`-fel):

```latte
{if $user->isLoggedIn()}<a n:href="Edit:create">Bejegyzés létrehozása</a>{/if}
```

Ugyanígy elrejtjük a linket az `app/Presentation/Post/show.latte` sablonban is.


Bejelentkezési link
===================

Hogyan jutunk el valójában a bejelentkezési oldalra? Nincs rá mutató link. Adjunk hozzá egyet az `@layout.latte` sablonhoz. Próbáljon megfelelő helyet találni - szinte bárhol lehet.

```latte .{file:app/Presentation/@layout.latte}
...
<ul class="navig">
	<li><a n:href="Home:">Cikkek</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Kijelentkezés</a></li>
	{else}
		<li><a n:href="Sign:in">Bejelentkezés</a></li>
	{/if}
</ul>
...
```

Ha a felhasználó nincs bejelentkezve, a "Bejelentkezés" link jelenik meg. Ellenkező esetben a "Kijelentkezés" link jelenik meg. Ezt az akciót is hozzáadjuk a `SignPresenter`-hez.

Mivel a felhasználót kijelentkezés után azonnal átirányítjuk, nincs szükség sablonra. A kijelentkezés így néz ki:

```php .{file:app/Presentation/Sign/SignPresenter.php}
public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('A kijelentkezés sikeres volt.');
	$this->redirect('Home:');
}
```

Csak a `logout()` metódus hívódik meg, majd megjelenik egy szép üzenet, amely megerősíti a sikeres kijelentkezést.


Összegzés
=========

Van linkünk a bejelentkezéshez és a felhasználó kijelentkezéséhez is. Az ellenőrzéshez a beépített authentikátort használtuk, és a bejelentkezési adatokat a konfigurációs fájlban tároljuk, mivel ez egy egyszerű tesztalkalmazás. Biztosítottuk a szerkesztési űrlapokat is, így csak a bejelentkezett felhasználók adhatnak hozzá és szerkeszthetnek bejegyzéseket.

.[note]
Itt olvashat többet a [felhasználók bejelentkeztetéséről |security:authentication] és a [jogosultságok ellenőrzéséről |security:authorization].

{{priority: -1}}
{{sitename: Nette Quickstart}}

Authentikáció

A Nette módot biztosít az authentikáció programozására az oldalainkon, de semmire sem kényszerít minket. Az implementáció kizárólag rajtunk múlik. A Nette tartalmazza a Nette\Security\Authenticator interfészt, amely csak egyetlen authenticate metódust követel meg, amely a felhasználót bármilyen általunk kívánt módon ellenőrzi.

Számos lehetőség van arra, hogyan lehet egy felhasználót hitelesíteni. A leggyakoribb hitelesítési mód a jelszó használata (a felhasználó megadja a nevét vagy e-mail címét és jelszavát), de vannak más módszerek is. Talán ismeri a „Bejelentkezés Facebookkal“ típusú gombokat, vagy a Google/Twitter/GitHub segítségével történő bejelentkezést néhány oldalon. A Nette segítségével bármilyen bejelentkezési módszerünk lehet, vagy akár kombinálhatjuk is őket. Ez csak rajtunk múlik.

Általában saját authentikátort írnánk, de ehhez az egyszerű kis bloghoz a beépített authentikátort fogjuk használni, amely a konfigurációs fájlban tárolt jelszó és felhasználónév alapján jelentkeztet be. Tesztelési célokra jól használható. Adjunk hozzá tehát a következő security szekciót a config/common.neon konfigurációs fájlhoz:

security:
	users:
		admin: secret  # felhasználó 'admin', jelszó 'secret'

A Nette automatikusan létrehoz egy szolgáltatást a DI konténerben.

Bejelentkezési űrlap

Most már készen áll az authentikáció, és elő kell készítenünk a felhasználói felületet a bejelentkezéshez. Hozzunk létre tehát egy új presentert SignPresenter néven, amely:

  • megjeleníti a bejelentkezési űrlapot (felhasználónévvel és jelszóval)
  • az űrlap elküldése után ellenőrzi a felhasználót
  • lehetőséget biztosít a kijelentkezésre

Kezdjük a bejelentkezési űrlappal. Már tudjuk, hogyan működnek az űrlapok a presenterekben. Hozzunk létre tehát egy SignPresenter presentert, és írjuk meg a createComponentSignInForm metódust. Valahogy így kellene kinéznie:

<?php
namespace App\Presentation\Sign;

use Nette;
use Nette\Application\UI\Form;

final class SignPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentSignInForm(): Form
	{
		$form = new Form;
		$form->addText('username', 'Felhasználónév:')
			->setRequired('Kérjük, töltse ki a felhasználónevét.');

		$form->addPassword('password', 'Jelszó:')
			->setRequired('Kérjük, töltse ki a jelszavát.');

		$form->addSubmit('send', 'Bejelentkezés');

		$form->onSuccess[] = $this->signInFormSucceeded(...);
		return $form;
	}
}

Vannak mezők a felhasználónévhez és a jelszóhoz.

Sablon

Az űrlap az in.latte sablonban fog renderelődni:

{block content}
<h1 n:block=title>Bejelentkezés</h1>

{control signInForm}

Bejelentkezési callback

Ezután hozzáadjuk a felhasználó bejelentkezéséhez szükséges callbacket, amely az űrlap sikeres elküldése után azonnal meghívódik.

A callback csak átveszi a felhasználó által kitöltött felhasználónevet és jelszót, és átadja azokat az authentikátornak. Bejelentkezés után átirányítunk a kezdőoldalra.

private function signInFormSucceeded(Form $form, \stdClass $data): void
{
	try {
		$this->getUser()->login($data->username, $data->password);
		$this->redirect('Home:');

	} catch (Nette\Security\AuthenticationException $e) {
		$form->addError('Helytelen felhasználónév vagy jelszó.');
	}
}

User::login() metódus kivételt dob, ha a felhasználónév és a jelszó nem egyezik a konfigurációs fájlban szereplő adatokkal. Ahogy már tudjuk, ez piros hibaoldalt eredményezhet, vagy éles módban egy szerverhibáról tájékoztató üzenetet. Ezt azonban nem akarjuk. Ezért elkapjuk ezt a kivételt, és egy szép, felhasználóbarát hibaüzenetet adunk át az űrlapnak.

Amint hiba történik az űrlapon, az űrlapot tartalmazó oldal újrarajzolódik, és az űrlap felett megjelenik egy szép üzenet, amely tájékoztatja a felhasználót, hogy rossz felhasználónevet vagy jelszót adott meg.

Presenterek védelme

Biztosítsuk a bejegyzések hozzáadására és szerkesztésére szolgáló űrlapot. Ez az EditPresenter presenterben van definiálva. A cél az, hogy megakadályozzuk a nem bejelentkezett felhasználók hozzáférését az oldalhoz.

Létrehozunk egy startup() metódust, amely azonnal elindul a presenter életciklusának elején. Ez átirányítja a nem bejelentkezett felhasználókat a bejelentkezési űrlapra.

public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}

Linkek elrejtése

Az illetéktelen felhasználó már nem láthatja a create vagy edit oldalt, de továbbra is láthatja a rájuk mutató linkeket. Ezeket is el kellene rejtenünk. Egy ilyen link az app/Presentation/Home/default.latte sablonban található, és csak a bejelentkezett felhasználók láthatják.

Elrejthetjük egy n:attribútum használatával, amelynek neve n:if. Ha ez a feltétel false, az egész <a> tag, beleértve a tartalmát is, rejtve marad.

<a n:href="Edit:create" n:if="$user->isLoggedIn()">Bejegyzés létrehozása</a>

ami a következő jelölés rövidítése (nem összetévesztendő a tag-if-fel):

{if $user->isLoggedIn()}<a n:href="Edit:create">Bejegyzés létrehozása</a>{/if}

Ugyanígy elrejtjük a linket az app/Presentation/Post/show.latte sablonban is.

Hogyan jutunk el valójában a bejelentkezési oldalra? Nincs rá mutató link. Adjunk hozzá egyet az @layout.latte sablonhoz. Próbáljon megfelelő helyet találni – szinte bárhol lehet.

...
<ul class="navig">
	<li><a n:href="Home:">Cikkek</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Kijelentkezés</a></li>
	{else}
		<li><a n:href="Sign:in">Bejelentkezés</a></li>
	{/if}
</ul>
...

Ha a felhasználó nincs bejelentkezve, a „Bejelentkezés“ link jelenik meg. Ellenkező esetben a „Kijelentkezés“ link jelenik meg. Ezt az akciót is hozzáadjuk a SignPresenter-hez.

Mivel a felhasználót kijelentkezés után azonnal átirányítjuk, nincs szükség sablonra. A kijelentkezés így néz ki:

public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('A kijelentkezés sikeres volt.');
	$this->redirect('Home:');
}

Csak a logout() metódus hívódik meg, majd megjelenik egy szép üzenet, amely megerősíti a sikeres kijelentkezést.

Összegzés

Van linkünk a bejelentkezéshez és a felhasználó kijelentkezéséhez is. Az ellenőrzéshez a beépített authentikátort használtuk, és a bejelentkezési adatokat a konfigurációs fájlban tároljuk, mivel ez egy egyszerű tesztalkalmazás. Biztosítottuk a szerkesztési űrlapokat is, így csak a bejelentkezett felhasználók adhatnak hozzá és szerkeszthetnek bejegyzéseket.

Itt olvashat többet a felhasználók bejelentkeztetéséről és a jogosultságok ellenőrzéséről.