-
-
Notifications
You must be signed in to change notification settings - Fork 277
Expand file tree
/
Copy pathauthentication.texy
More file actions
179 lines (121 loc) · 7.08 KB
/
authentication.texy
File metadata and controls
179 lines (121 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
Uwierzytelnianie
****************
Nette dostarcza sposób na zaprogramowanie uwierzytelniania na naszych stronach, ale do niczego nas nie zmusza. Implementacja zależy wyłącznie od nas. Nette zawiera interfejs `Nette\Security\Authenticator`, który wymaga tylko jednej metody `authenticate`, która weryfikuje użytkownika w dowolny sposób, jaki zechcemy.
Istnieje wiele możliwości, jak użytkownik może zostać uwierzytelniony. Najczęstszym sposobem uwierzytelniania jest użycie hasła (użytkownik podaje swoją nazwę lub e-mail i hasło), ale są też inne sposoby. Być może znasz przyciski typu "Zaloguj przez Facebooka" lub logowanie za pomocą Google/Twitter/GitHub na niektórych stronach. Z Nette możemy mieć dowolną metodę logowania, lub możemy je nawet łączyć. To zależy tylko od nas.
Zwykle napisalibyśmy własny authenticator, ale dla tego prostego małego bloga użyjemy wbudowanego authenticatora, który loguje na podstawie hasła i nazwy użytkownika zapisanych w pliku konfiguracyjnym. Przydaje się do celów testowych. Dodajmy więc następującą sekcję `security` do pliku konfiguracyjnego `config/common.neon`:
```neon .{file:config/common.neon}
security:
users:
admin: secret # użytkownik 'admin', hasło 'secret'
```
Nette automatycznie utworzy usługę w kontenerze DI.
Formularz logowania
===================
Teraz mamy przygotowane uwierzytelnianie i musimy przygotować interfejs użytkownika do logowania. Utwórzmy więc nowy presenter o nazwie `SignPresenter`, który:
- wyświetli formularz logowania (z nazwą użytkownika i hasłem)
- po wysłaniu formularza zweryfikuje użytkownika
- zapewni możliwość wylogowania
Zaczniemy od formularza logowania. Już wiemy, jak działają formularze w prezenterach. Utworzymy więc presenter `SignPresenter` i napiszemy metodę `createComponentSignInForm`. Powinien wyglądać mniej więcej tak:
```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', 'Nazwa użytkownika:')
->setRequired('Proszę podać swoją nazwę użytkownika.');
$form->addPassword('password', 'Hasło:')
->setRequired('Proszę podać swoje hasło.');
$form->addSubmit('send', 'Zaloguj');
$form->onSuccess[] = $this->signInFormSucceeded(...);
return $form;
}
}
```
Są tu pola na nazwę użytkownika i hasło.
Szablon
-------
Formularz będzie renderowany w szablonie `in.latte`:
```latte .{file:app/Presentation/Sign/in.latte}
{block content}
<h1 n:block=title>Logowanie</h1>
{control signInForm}
```
Callback logowania
------------------
Następnie dodamy callback do logowania użytkownika, który będzie wywoływany zaraz po pomyślnym wysłaniu formularza.
Callback jedynie pobierze nazwę użytkownika i hasło, które użytkownik wypełnił, i przekaże je do authenticatora. Po zalogowaniu przekierujemy na stronę główną.
```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('Nieprawidłowa nazwa użytkownika lub hasło.');
}
}
```
Metoda [User::login() |api:Nette\Security\User::login()] rzuci wyjątek, jeśli nazwa użytkownika i hasło nie zgadzają się z danymi w pliku konfiguracyjnym. Jak już wiemy, może to skutkować czerwoną stroną błędu lub, w trybie produkcyjnym, komunikatem informującym o błędzie serwera. Tego jednak nie chcemy. Dlatego przechwycimy ten wyjątek i przekażemy ładny, przyjazny dla użytkownika komunikat błędu do formularza.
Gdy w formularzu wystąpi błąd, strona z formularzem zostanie ponownie wyrenderowana, a nad formularzem pojawi się ładny komunikat informujący użytkownika, że podał złą nazwę użytkownika lub hasło.
Zabezpieczenie prezenterów
==========================
Zabezpieczymy formularz do dodawania i edytowania postów. Jest on zdefiniowany w presenterze `EditPresenter`. Celem jest uniemożliwienie dostępu do strony użytkownikom, którzy nie są zalogowani.
Utworzymy metodę `startup()`, która uruchamia się natychmiast na początku [cyklu życia presentera |application:presenters#Cykl życia presentera]. Przekieruje ona niezalogowanych użytkowników do formularza logowania.
```php .{file:app/Presentation/Edit/EditPresenter.php}
public function startup(): void
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
```
Ukrywanie linków
----------------
Nieautoryzowany użytkownik już nie może zobaczyć strony `create` ani `edit`, ale nadal może widzieć do nich linki. Te również powinniśmy ukryć. Jeden taki link znajduje się w szablonie `app/Presentation/Home/default.latte` i powinni go widzieć tylko zalogowani użytkownicy.
Możemy go ukryć za pomocą *n:atrybutu* o nazwie `n:if`. Jeśli ten warunek jest `false`, cały tag `<a>`, wraz z zawartością, pozostanie ukryty.
```latte
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Utwórz post</a>
```
co jest skrótem następującego zapisu (nie mylić z `tag-if`):
```latte
{if $user->isLoggedIn()}<a n:href="Edit:create">Utwórz post</a>{/if}
```
W ten sam sposób ukryjemy również link w szablonie `app/Presentation/Post/show.latte`.
Link do logowania
=================
Jak właściwie dostaniemy się na stronę logowania? Nie ma tu żadnego linku, który by do niej prowadził. Dodajmy go więc do szablonu `@layout.latte`. Spróbuj znaleźć odpowiednie miejsce - może to być prawie wszędzie.
```latte .{file:app/Presentation/@layout.latte}
...
<ul class="navig">
<li><a n:href="Home:">Artykuły</a></li>
{if $user->isLoggedIn()}
<li><a n:href="Sign:out">Wyloguj</a></li>
{else}
<li><a n:href="Sign:in">Zaloguj</a></li>
{/if}
</ul>
...
```
Jeśli użytkownik nie jest zalogowany, wyświetli się link "Zaloguj". W przeciwnym razie wyświetli się link "Wyloguj". Tę akcję również dodamy do `SignPresenter`.
Ponieważ użytkownika po wylogowaniu natychmiast przekierowujemy, nie jest potrzebny żaden szablon. Wylogowanie wygląda tak:
```php .{file:app/Presentation/Sign/SignPresenter.php}
public function actionOut(): void
{
$this->getUser()->logout();
$this->flashMessage('Wylogowanie powiodło się.');
$this->redirect('Home:');
}
```
Wywołuje się tylko metodę `logout()`, a następnie wyświetla się ładny komunikat potwierdzający pomyślne wylogowanie.
Podsumowanie
============
Mamy link do logowania i wylogowania użytkownika. Do uwierzytelniania użyliśmy wbudowanego authenticatora, a dane logowania mamy w pliku konfiguracyjnym, ponieważ jest to prosta aplikacja testowa. Zabezpieczyliśmy również formularze edycji, więc dodawać i edytować posty mogą tylko zalogowani użytkownicy.
.[note]
Tutaj możesz przeczytać więcej o [logowaniu użytkowników |security:authentication] i [autoryzacji |security:authorization].
{{priority: -1}}