Аутентификация
Nette предоставляет способ программирования аутентификации на нашем
сайте, но не заставляет нас ничего делать. Реализация зависит от нас.
Nette включает интерфейс Nette\Security\Authenticator
, который требует только
один метод authenticate
для аутентификации пользователя, как
мы хотим.
Существует множество способов, с помощью которых пользователь может аутентифицировать себя. Наиболее распространенным является аутентификация на основе пароля (пользователь указывает свое имя или электронную почту и пароль), но существуют и другие способы. Возможно, вы видели кнопки типа «Войти с помощью Facebook/Google/Twitter/GitHub и т. д.» на многих сайтах. С Nette мы можем использовать любой метод входа в систему, а также комбинировать их. Это зависит от нас.
Обычно мы пишем собственный аутентификатор, но для этого простого
блога мы воспользуемся встроенным аутентификатором, который
осуществляет вход на основе пароля и имени пользователя, хранящихся в
конфигурационном файле. Это хорошо подходит для тестирования.
Добавьте секцию security в конфигурационный файл config/common.neon
:
security:
users:
admin: secret # логин 'admin', пароль 'secret'
Nette автоматически создаст службу в контейнере DI.
Форма регистрации
Теперь у нас есть готовая аутентификация, и нам нужно подготовить пользовательский интерфейс для входа в систему. Поэтому давайте создадим новый презентер под названием SignPresenter, который будет:
- отображать форму входа в систему (запрашивается имя пользователя и пароль)
- аутентифицировать пользователя при отправке формы
- обеспечивать выход из системы
Давайте начнем с формы входа в систему. Вы уже знаете, как работают
формы в презентере. Создайте 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()
и затем показывает пользователю
красивое сообщение.
Подведём итог
У нас есть ссылка для входа в систему, а также для выхода пользователя из системы. Для аутентификации мы использовали встроенный аутентификатор, а данные для входа находятся в конфигурационном файле, поскольку это простое тестовое приложение. Мы также защитили формы редактирования, чтобы только вошедшие в систему пользователи могли добавлять и редактировать сообщения.
Здесь вы можете прочитать больше о вход пользователя и авторизация.