Přihlašování uživatelů (Autentizace)
Pomalu žádná webová aplikace se neobejde bez mechanismu přihlašování uživatelů a ověřování uživatelských oprávnění. V této kapitole si povíme o:
- přihlašování a odhlašování uživatelů
- vlastních autentikátorech
V příkladech budeme používat objekt třídy Nette\Security\User, který představuje aktuálního
uživatele a ke kterému se dostanete tak, že si jej necháte předat pomocí dependency injection. V presenterech stačí jen zavolat
$user = $this->getUser()
.
Autentizace
Autentizací se rozumí přihlašování uživatelů, tedy proces, při kterém se ověřuje, zda je uživatel opravdu
tím, za koho se vydává. Obvykle se prokazuje uživatelským jménem a heslem. Ověření provede tzv. autentikátor. Pokud přihlášení selže, vyhodí se
Nette\Security\AuthenticationException
.
Tímto způsobem uživatele odhlásíte:
A zjištění, že je přihlášen:
Velmi jednoduché, viďte? A všechny bezpečnostní aspekty řeší Nette za vás.
V presenterech můžete ověřit přihlášení v metodě startup()
a nepřihlášeného uživatele
přesměrovat na přihlašovací stránku.
Expirace
Přihlášení uživatele expiruje společně s expirací úložiště,
kterým je obvykle session (viz nastavení expirace session). Lze ale nastavit
i kratší časový interval, po jehož uplynutí dojde k odhlášení uživatele. K tomu slouží metoda
setExpiration()
, která se volá před login()
. Jako parametr uveďte řetězec
s relativním časem:
Jestli byl uživatel odhlášen z důvodu vypršení časového intervalu prozradí metoda
$user->getLogoutReason()
, která vrací buď konstantu Nette\Security\IUserStorage::LOGOUT_INACTIVITY
(vypršel časový limit) nebo IUserStorage::LOGOUT_MANUAL
(odhlášen metodou logout()
).
Autentikátor
Jde o objekt, který ověřuje přihlašovací údaje, tedy zpravidla jméno a heslo. Triviální podobou je třída Nette\Security\SimpleAuthenticator, kterou můžeme nadefinovat v konfiguraci:
Toto řešení je vhodné spíš pro testovací účely. Ukážeme si, jak vytvořit autentikátor, který bude ověřovat přihlašovací údaje oproti databázové tabulce.
Autentikátor je objekt implementující rozhraní Nette\Security\IAuthenticator s metodou
authenticate()
. Jejím úkolem je buď vrátit tzv. identitu nebo vyhodit výjimku
Nette\Security\AuthenticationException
. Bylo by možné u ní ještě uvést chybový kód k jemnějšímu
rozlišení vzniklé situace: IAuthenticator::IDENTITY_NOT_FOUND
a
IAuthenticator::INVALID_CREDENTIAL
.
Třída MyAuthenticator komunikuje s databází prostřednictvím Nette
Database Explorer a pracuje s tabulkou users
, kde je v sloupci username
přihlašovací jméno
uživatele a ve sloupci password
otisk hesla. Po ověření jména a hesla vrací
identitu, která nese ID uživatele, jeho roli (sloupec role
v tabulce), o které si více řekneme později, a pole s dalšími daty (v našem případě uživatelské jméno).
Autentikátor ještě přidáme do konfigurace jako službu DI kontejneru:
Události $onLoggedIn, $onLoggedOut
Objekt Nette\Security\User
má události
$onLoggedIn
a $onLoggedOut
, můžete tedy přidat callbacky, které se vyvolají po úspěšném
přihlášení resp. po odhlášení uživatele.
Identita
Identita představuje soubor informací o uživateli, který vrací autentikátor a který se následně uchovává v session
a získáváme jej pomocí $user->getIdentity()
. Můžeme tedy získat id, role a další uživatelská data, tak
jak jsme si je předali v autentikátoru:
Co je důležité, tak že při odhlášení pomocí $user->logout()
se identita nesmaže a je nadále
k dispozici. Takže ačkoliv má uživatel identitu, nemusí být přihlášený. Pokud bychom chtěli identitu explicitně
smazat, odhlásíme uživatele voláním logout(true)
.
Díky tomu můžete nadále předpokládat, který uživatel je u počítače a například mu v e-shopu zobrazovat personalizované nabídky, nicméně zobrazit mu jeho osobní údaje můžete až po přihlášení.
Identita je objekt implementující rozhraní Nette\Security\IIdentity, výchozí implementací je Nette\Security\Identity. A jak bylo zmíněno, udržuje se v session, takže pokud tedy například změníme roli některého z přihlášených uživatelů, zůstanou stará data v jeho identitě až do jeho opětovného přihlášení.
Více nezávislých přihlášení
Souběžně je možné v rámci jednoho webu a jedné session mít několik nezávislých přihlašujících se uživatelů. Pokud například chceme mít na webu oddělenou autentizaci pro administraci a veřejnou část, stačí každé z nich nastavit vlastní název:
Je důležité pamatovat na to, abychom jmenný prostor nastavili vždy na všech místech patřících do dané části. Pakliže používáme presentery, nastavíme jmenný prostor ve společném předkovi pro danou část – obvykle BasePresenter. Učiníme tak rozšířením metody checkRequirements():
Více autentikátorů
Rozdělení aplikace na části s nezávislým přihlašováním většinou vyžaduje také různé autentikátory. Jakmile
bychom však v konfiguraci služeb zaregistrovali dvě třídy implementující IAuthenticator, Nette by nevědělo, který
z nich automaticky přiřadit objektu Nette\Security\User
, a zobrazilo by chybu. Proto musíme pro autentikátory
autowiring omezit tak, aby fungoval, jen když si někdo vyžádá konkrétní třídu, např. FrontAuthenticator, čehož
docílíme volbou autowired: self
:
Autentikátor objektu User nastavíme před voláním metody login(), takže obvykle v kódu formuláře, který ho přihlašuje: