Avtentikacija
Nette zagotavlja način za programiranje avtentikacije na naših straneh, vendar nas v nič ne sili. Implementacija je samo
naša. Nette vsebuje vmesnik Nette\Security\Authenticator
, ki zahteva samo eno metodo authenticate
, ki
preverja uporabnika na kakršen koli način želimo.
Obstaja veliko možnosti, kako je lahko uporabnik preverjen. Najpogostejši način preverjanja je z geslom (uporabnik navede svoje ime ali e-pošto in geslo), vendar obstajajo tudi drugi načini. Morda poznate gumbe tipa “Prijavi se s Facebookom” ali prijavo z Google/Twitter/GitHub na nekaterih straneh. Z Nette lahko imamo katero koli metodo prijave ali pa jih lahko celo kombiniramo. Odvisno je samo od nas.
Običajno bi napisali lasten avtentikator, vendar bomo za ta preprost majhen blog uporabili vgrajeni avtentikator, ki prijavlja
na podlagi gesla in uporabniškega imena, shranjenega v konfiguracijski datoteki. Primeren je za testne namene. Dodajmo torej
naslednji odsek security
v konfiguracijsko datoteko config/common.neon
:
security:
users:
admin: secret # uporabnik 'admin', geslo 'secret'
Nette samodejno ustvari storitev v DI vsebniku.
Prijavni obrazec
Zdaj imamo avtentikacijo pripravljeno in moramo pripraviti uporabniški vmesnik za prijavo. Ustvarimo torej nov presenter
z imenom SignPresenter
, ki:
- prikaže prijavni obrazec (z uporabniškim imenom in geslom)
- po oddaji obrazca preveri uporabnika
- zagotovi možnost odjave
Začnimo s prijavnim obrazcem. Že vemo, kako obrazci v presenterjih delujejo. Ustvarimo si torej presenter
SignPresenter
in zapišemo metodo createComponentSignInForm
. Moral bi izgledati nekako takole:
<?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', 'Uporabniško ime:')
->setRequired('Prosimo, vnesite svoje uporabniško ime.');
$form->addPassword('password', 'Geslo:')
->setRequired('Prosimo, vnesite svoje geslo.');
$form->addSubmit('send', 'Prijavi se');
$form->onSuccess[] = $this->signInFormSucceeded(...);
return $form;
}
}
Tukaj sta polji za uporabniško ime in geslo.
Predloga
Obrazec se bo izrisal v predlogi in.latte
:
{block content}
<h1 n:block=title>Prijava</h1>
{control signInForm}
Povratni klic za prijavo
Nato dodamo povratni klic za prijavo uporabnika, ki bo klican takoj po uspešni oddaji obrazca.
Povratni klic samo prevzame uporabniško ime in geslo, ki ju je uporabnik izpolnil, in ju preda avtentikatorju. Po prijavi preusmerimo na začetno stran.
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('Napačno uporabniško ime ali geslo.');
}
}
Metoda User::login() vrže izjemo, če se uporabniško ime in geslo ne ujemata s podatki v konfiguracijski datoteki. Kot že vemo, lahko to privede do rdeče strani z napako ali v produkcijskem načinu do sporočila o napaki strežnika. Tega pa nočemo. Zato to izjemo ujamemo in predamo lepo, uporabniku prijazno sporočilo o napaki v obrazec.
Ko pride do napake v obrazcu, se stran z obrazcem ponovno izriše in nad obrazcem se prikaže lepo sporočilo, ki uporabnika obvešča, da je vnesel napačno uporabniško ime ali geslo.
Zavarovanje presenterjev
Zavarovali bomo obrazec za dodajanje in urejanje prispevkov. Ta je definiran v presenterju EditPresenter
. Cilj je
onemogočiti dostop do strani uporabnikom, ki niso prijavljeni.
Ustvarimo metodo startup()
, ki se zažene takoj na začetku življenjskega cikla presenterja. Ta
preusmeri neprijavljene uporabnike na obrazec za prijavo.
public function startup(): void
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
Skrivanje povezav
Nepooblaščen uporabnik že ne more videti strani create
ali edit
, vendar še vedno lahko vidi
povezave do njih. Te bi morali tudi skriti. Ena taka povezava je v predlogi app/Presentation/Home/default.latte
in
bi jo morali videti samo prijavljeni uporabniki.
Lahko jo skrijemo z uporabo n:atributa z imenom n:if
. Če je ta pogoj false
, celotna značka
<a>
, vključno z vsebino, ostane skrita.
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Ustvari prispevek</a>
kar je okrajšava naslednjega zapisa (ne zamenjujte z tag-if
):
{if $user->isLoggedIn()}<a n:href="Edit:create">Ustvari prispevek</a>{/if}
Na enak način skrijemo tudi povezavo v predlogi app/Presentation/Post/show.latte
.
Povezava za prijavo
Kako pravzaprav pridemo do prijavne strani? Ni nobene povezave, ki bi vodila nanjo. Torej jo dodajmo v predlogo
@layout.latte
. Poskusite najti primerno mesto – lahko je skoraj kjerkoli.
...
<ul class="navig">
<li><a n:href="Home:">Članki</a></li>
{if $user->isLoggedIn()}
<li><a n:href="Sign:out">Odjava</a></li>
{else}
<li><a n:href="Sign:in">Prijava</a></li>
{/if}
</ul>
...
Če uporabnik ni prijavljen, se prikaže povezava “Prijavi se”. V nasprotnem primeru se prikaže povezava “Odjavi se”.
To dejanje dodamo tudi v SignPresenter
.
Ker uporabnika po odjavi takoj preusmerimo, ni potrebna nobena predloga. Odjava izgleda takole:
public function actionOut(): void
{
$this->getUser()->logout();
$this->flashMessage('Odjava je bila uspešna.');
$this->redirect('Home:');
}
Samo pokliče se metoda logout()
in nato se prikaže lepo sporočilo, ki potrjuje uspešno odjavo.
Povzetek
Imamo povezavo za prijavo in tudi odjavo uporabnika. Za preverjanje smo uporabili vgrajeni avtentikator in prijavne podatke imamo v konfiguracijski datoteki, saj gre za preprosto testno aplikacijo. Prav tako smo zavarovali urejevalne obrazce, tako da lahko prispevke dodajajo in urejajo samo prijavljeni uporabniki.
Tukaj si lahko preberete več o prijavljanju uporabnikov in Preverjanju dovoljenj.