Nette Documentation Preview

syntax
Удостоверяване
**************

Nette предоставя начин за програмиране на удостоверяване на нашия уебсайт, но не ни принуждава да правим нищо. Изпълнението зависи от нас. Nette включва интерфейс `Nette\Security\Authenticator`, който изисква само един метод `authenticate` за удостоверяване на потребителя, както искаме.

Има много начини, по които потребителят може да се удостовери. Най-разпространеният метод е *удостоверяване с парола* (потребителят посочва своето име или имейл и парола), но има и други методи. Може би сте виждали бутони като "Влезте във Facebook/Google/Twitter/GitHub и т.н." в много уебсайтове. С Nette можем да използваме всеки метод за влизане, който желаем, и можем да ги комбинираме. Това зависи от нас.

Обикновено пишем собствен автентификатор, но за този прост блог ще използваме вградения автентификатор, който влиза в системата въз основа на паролата и потребителското име, записани в конфигурационния файл. Това работи добре за целите на тестването. Добавяне на раздел *security* в конфигурационния файл `config/common.neon`:


```neon .{file:config/common.neon}
security:
	users:
		admin: secret # вход 'admin', парола 'secret'
```

Nette автоматично ще създаде услуга в контейнера DI.


Регистрационен формуляр .[#toc-sign-in-form]
============================================

Вече имаме готово удостоверяване и трябва да подготвим потребителския интерфейс за влизане в системата. Затова нека създадем нов презентатор, наречен *SignPresenter*, който ще:

- показване на формуляра за вход (изискват се потребителско име и парола)
- Удостоверяване на автентичността на потребителя при подаване на формуляра
- Осигуряване на изход от системата

Нека започнем с формата за вход. Вече знаете как работят формулярите в програмата Presenter. Създайте `SignPresenter` и метод `createComponentSignInForm`. Това трябва да изглежда по следния начин:

```php .{file:app/UI/Sign/SignPresenter.php}
<?php
namespace App\UI\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', 'Имя пользователя:')
			->setRequired('Пожалуйста, введите ваше имя.');

		$form->addPassword('password', 'Пароль:')
			->setRequired('Пожалуйста, введите ваш пароль.');

		$form->addSubmit('send', 'Войти');

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

Вече имаме въведени потребителско име и парола.


Шаблон .[#toc-template]
-----------------------

Формулярът ще бъде показан в шаблона `app/UI/Sign/in.latte`.

```latte .{file:app/UI/Sign/in.latte}
{block content}
<h1 n:block=title>Войти</h1>

{control signInForm}
```


Обслужване на вход .[#toc-login-handler]
----------------------------------------

След това ще добавим *обработчик на формуляра* за влизане на потребителя, който ще бъде извикан веднага след успешното изпращане на формуляра.

Обслужващият модул просто взема потребителското име и паролата, които потребителят е въвел, и ги предава на удостоверителя. След като влезете в системата, ще ви пренасочим към началната страница:

```php .{file:app/UI/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('Неправильные логин или пароль.');
	}
}
```

Методът [User::login( |api:Nette\Security\User::login()] ) трябва да хвърли изключение, ако потребителското име или паролата не съвпадат с тези, които определихме по-рано. Както вече знаем, това ще доведе до червена страница за грешка на [Tracy |tracy:] или, в производствен режим, до съобщение за грешка на вътрешния сървър. Но ние не искаме това. Така че прихващаме изключението и добавяме хубаво, дружелюбно съобщение за грешка към формуляра.

Когато във формуляра възникне грешка, страницата на формуляра ще бъде показана отново, а над формуляра ще се появи хубаво съобщение, което ще информира потребителя, че е въвел грешно потребителско име или парола.


Сигурност на водещия .[#toc-security-of-presenters]
===================================================

Подсигурете формата за добавяне и редактиране на публикации. Целта е да се предотврати достъпът на неоторизирани потребители до страницата.

Ще създадем метод `startup()`, който се изпълнява веднага в началото на [жизнения цикъл на презентатора |application:presenters#Life-Cycle-of-Presenter]. Това пренасочва нерегистрираните потребители към формата за вход.

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

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


Скриване на връзки .[#toc-hide-links]
-------------------------------------

Неоторизиран потребител вече не може да вижда страниците за създаване и редактиране, но все още може да вижда връзките, които сочат към тях. Нека скрием и тях. Една такава връзка се намира на адрес `app/UI/Home/default.latte`, като тя трябва да бъде видима само ако потребителят е влязъл в системата.

Можем да го скрием, като използваме *n:атрибута*, наречен `n:if`. Ако изявлението в него е `false`, тогава целият таг `<a>` и съдържанието му няма да бъде показано:

```latte
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Создать пост</a>
```

това е съкращение (да не се бърка с `tag-if`):

```latte
{if $user->isLoggedIn()}<a n:href="Edit:create">Создать пост</a>{/if}
```

По същия начин скрийте връзката за редактиране, намираща се на адрес `app/UI/Post/show.latte`.


Връзка към формуляра за влизане .[#toc-login-form-link]
=======================================================

Здравейте, но как да стигнем до страницата за вход? Няма връзка, която да сочи към него. Нека да го добавим към файла с шаблона `app/UI/@layout.latte`. Опитайте се да намерите добро място, може да е всяко място, което ви харесва най-много.

```latte .{file:app/UI/@layout.latte}
...
<ul class="navig">
	<li><a n:href="Home:">Главная</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Выйти</a></li>
	{else}
		<li><a n:href="Sign:in">Войти</a></li>
	{/if}
</ul>
...
```

Ако потребителят все още не е влязъл в системата, той ще види връзка "Вход". В противен случай ще се вижда връзката "Изход". Добавете това действие в `SignPresenter`.

Действието за излизане от системата изглежда по следния начин и тъй като пренасочваме потребителя веднага, няма нужда от шаблон на изгледа:

```php .{file:app/UI/Sign/SignPresenter.php}
public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('Вы вышли.');
	$this->redirect('Home:');
}
```

Той просто извиква метода `logout()` и след това показва на потребителя хубаво съобщение.


За да обобщим .[#toc-summary]
=============================

Имаме връзка за влизане и връзка за излизане на потребителя. Използвахме вградения автентификатор за удостоверяване, а данните за вход са в конфигурационния файл, тъй като това е просто тестово приложение. Също така защитихме формите за редактиране, така че само влезлите в системата потребители да могат да добавят и редактират съобщения.

.[note]
Тук можете да прочетете повече за [user login |security:authentication] и [authorization |security:authorization].

{{priority: -1}}
{{sitename: Быстрый старт с Nette}}

Удостоверяване

Nette предоставя начин за програмиране на удостоверяване на нашия уебсайт, но не ни принуждава да правим нищо. Изпълнението зависи от нас. Nette включва интерфейс Nette\Security\Authenticator, който изисква само един метод authenticate за удостоверяване на потребителя, както искаме.

Има много начини, по които потребителят може да се удостовери. Най-разпространеният метод е удостоверяване с парола (потребителят посочва своето име или имейл и парола), но има и други методи. Може би сте виждали бутони като „Влезте във Facebook/Google/Twitter/GitHub и т.н.“ в много уебсайтове. С Nette можем да използваме всеки метод за влизане, който желаем, и можем да ги комбинираме. Това зависи от нас.

Обикновено пишем собствен автентификатор, но за този прост блог ще използваме вградения автентификатор, който влиза в системата въз основа на паролата и потребителското име, записани в конфигурационния файл. Това работи добре за целите на тестването. Добавяне на раздел security в конфигурационния файл config/common.neon:

security:
	users:
		admin: secret # вход 'admin', парола 'secret'

Nette автоматично ще създаде услуга в контейнера DI.

Регистрационен формуляр

Вече имаме готово удостоверяване и трябва да подготвим потребителския интерфейс за влизане в системата. Затова нека създадем нов презентатор, наречен SignPresenter, който ще:

  • показване на формуляра за вход (изискват се потребителско име и парола)
  • Удостоверяване на автентичността на потребителя при подаване на формуляра
  • Осигуряване на изход от системата

Нека започнем с формата за вход. Вече знаете как работят формулярите в програмата Presenter. Създайте SignPresenter и метод createComponentSignInForm. Това трябва да изглежда по следния начин:

<?php
namespace App\UI\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', 'Имя пользователя:')
			->setRequired('Пожалуйста, введите ваше имя.');

		$form->addPassword('password', 'Пароль:')
			->setRequired('Пожалуйста, введите ваш пароль.');

		$form->addSubmit('send', 'Войти');

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

Вече имаме въведени потребителско име и парола.

Шаблон

Формулярът ще бъде показан в шаблона app/UI/Sign/in.latte.

{block content}
<h1 n:block=title>Войти</h1>

{control signInForm}

Обслужване на вход

След това ще добавим обработчик на формуляра за влизане на потребителя, който ще бъде извикан веднага след успешното изпращане на формуляра.

Обслужващият модул просто взема потребителското име и паролата, които потребителят е въвел, и ги предава на удостоверителя. След като влезете в системата, ще ви пренасочим към началната страница:

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('Неправильные логин или пароль.');
	}
}

Методът User::login( ) трябва да хвърли изключение, ако потребителското име или паролата не съвпадат с тези, които определихме по-рано. Както вече знаем, това ще доведе до червена страница за грешка на Tracy или, в производствен режим, до съобщение за грешка на вътрешния сървър. Но ние не искаме това. Така че прихващаме изключението и добавяме хубаво, дружелюбно съобщение за грешка към формуляра.

Когато във формуляра възникне грешка, страницата на формуляра ще бъде показана отново, а над формуляра ще се появи хубаво съобщение, което ще информира потребителя, че е въвел грешно потребителско име или парола.

Сигурност на водещия

Подсигурете формата за добавяне и редактиране на публикации. Целта е да се предотврати достъпът на неоторизирани потребители до страницата.

Ще създадем метод startup(), който се изпълнява веднага в началото на жизнения цикъл на презентатора. Това пренасочва нерегистрираните потребители към формата за вход.

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

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

Неоторизиран потребител вече не може да вижда страниците за създаване и редактиране, но все още може да вижда връзките, които сочат към тях. Нека скрием и тях. Една такава връзка се намира на адрес app/UI/Home/default.latte, като тя трябва да бъде видима само ако потребителят е влязъл в системата.

Можем да го скрием, като използваме n:атрибута, наречен n:if. Ако изявлението в него е false, тогава целият таг <a> и съдържанието му няма да бъде показано:

<a n:href="Edit:create" n:if="$user->isLoggedIn()">Создать пост</a>

това е съкращение (да не се бърка с tag-if):

{if $user->isLoggedIn()}<a n:href="Edit:create">Создать пост</a>{/if}

По същия начин скрийте връзката за редактиране, намираща се на адрес app/UI/Post/show.latte.

Здравейте, но как да стигнем до страницата за вход? Няма връзка, която да сочи към него. Нека да го добавим към файла с шаблона app/UI/@layout.latte. Опитайте се да намерите добро място, може да е всяко място, което ви харесва най-много.

...
<ul class="navig">
	<li><a n:href="Home:">Главная</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Выйти</a></li>
	{else}
		<li><a n:href="Sign:in">Войти</a></li>
	{/if}
</ul>
...

Ако потребителят все още не е влязъл в системата, той ще види връзка „Вход“. В противен случай ще се вижда връзката „Изход“. Добавете това действие в SignPresenter.

Действието за излизане от системата изглежда по следния начин и тъй като пренасочваме потребителя веднага, няма нужда от шаблон на изгледа:

public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('Вы вышли.');
	$this->redirect('Home:');
}

Той просто извиква метода logout() и след това показва на потребителя хубаво съобщение.

За да обобщим

Имаме връзка за влизане и връзка за излизане на потребителя. Използвахме вградения автентификатор за удостоверяване, а данните за вход са в конфигурационния файл, тъй като това е просто тестово приложение. Също така защитихме формите за редактиране, така че само влезлите в системата потребители да могат да добавят и редактират съобщения.

Тук можете да прочетете повече за user login и authorization.