Nette Framework: Přihlašování uživatelů
Pomalu žádná webová aplikace se neobejde bez mechanismu přihlašování uživatelů a ověřování uživatelských oprávnění. Pojďme se podívat, jak tyto úlohy řeší Nette Framework.
Seriál Začínáme s Nette Framework
- Nette Framework: AJAX (dokončení)
- Nette Framework: Sessions
- Nette Framework: Přihlašování uživatelů
- Nette Framework: Ověřování oprávnění a role
- Nette Framework: Neprůstřelné formuláře
Nejprve troška terminologie. Celé téma si rozdělíme do dvou okruhů: autentizace a autorizace. Pod 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á. V drtivé většině aplikací se ověřuje uživatelské jméno a heslo. Naopak při autorizaci se zjišťuje, zda má již autentizovaný uživatel dostatečná oprávnění pro přístup k určitému souboru či pro provedení nějaké akce. Autorizaci si necháme do příštího pokračování.
Chcete se naučit o Nette víc?
Akademie Root.cz školení Vývoj webových aplikací v Nette Framework. Kurz je určen všem programátorům v PHP, kteří se chtějí naučit tvořit webové aplikace rychle a kvalitně, bez bezpečnostních děr. Jako aplikační rámec slouží Nette Framework. Školí sám autor Nette – David Grudl. Máte zájem o jiné školení? Napište nám!
Autentizace
Přihlašování uživatelů je oblast velmi úzce související s ochranou osobních údajů a zabezpečením aplikace. Jelikož PHP nenabízí žádnou standardní implementaci, jde také bohužel o oblast bezbřehé programátorské „kreativity“. Lze se setkat s odstrašujícími případy, kdy programátoři například ukládají hesla do cookies a nebo vytvářejí jiné sofistikované bezpečnostní díry.
Nette Framework se snaží tuto díru zacelit. A zároveň přihlašování zjednodušit až na naprosté minimum. Tím jsou dvě metody authenticate() (přihlásit) a signOut() (odhlásit), plus dotazovací metoda isAuthenticated() sdělující, zda je uživatel nyní přihlášen.
O realizační stránku se stará třída Nette\Web\User. Ta je, stejně jako v případě Nette\Web\Session, singleton, proto nevytváříme její instanci přímo, ale vrátí ji metoda Environment::getUser(). Používá se zhruba tímto způsobem:
require 'Nette/loader.php';
$user = Environment::getUser();
// přihlášení uživatele
$username = ...
$password = ...
$user->authenticate($username, $password);
// je přihlášen?
echo $user->isAuthenticated() ? 'ano' : 'ne';
// odhlášení
$user->signOut();
Aby příklad fungoval, je potřeba napsat rutinu, která provede ověření uživatelského jména a hesla. Této rutině se říká autentizační handler a jde o objekt implementující rozhraní Nette\Security\IAuthenticator. To má jedinou metodu authenticate(). Implementace, která ověřuje přihlašovací údaje oproti databázové tabulce, může vypadat třeba takto:
require 'Nette/loader.php';
// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use Nette\Object, Nette\Security\IAuthenticator, Nette\Security\AuthenticationException, Nette\Security\Identity;
class MyAuthenticator extends Object implements IAuthenticator
{
public function authenticate(array $credentials)
{
$username = $credentials[self::USERNAME];
$password = sha1($credentials[self::PASSWORD] . $credentials[self::USERNAME]);
// přečteme záznam o uživateli z databáze
$row = dibi::fetch('SELECT realname, password FROM users WHERE login=%s', $username);
if (!$row) { // uživatel nenalezen?
throw new AuthenticationException("User '$username' not found.", self::IDENTITY_NOT_FOUND);
}
if ($row->password !== $password) { // hesla se neshodují?
throw new AuthenticationException("Invalid password.", self::INVALID_CREDENTIAL);
}
return new Identity($row->realname); // vrátíme identitu
}
}
Autentizační handler si zaslouží hlubší rozbor. Z pohledu návrhu aplikace podle vzoru MVP jde o součást modelu, přičemž samotnou autentizaci zpravidla iniciuje presenter. Nette Framework vás tak vede k oddělení ověření údajů od prezentační vrstvy.
Úkolem handleru je buď vrátit tzv. identitu v případě úspěchu, nebo vyhodit výjimku. Nette definuje výjimku Nette\Security\AuthenticationException a několik chybových kódů, které můžete využít k formálnímu popisu vzniklé chyby. (Nicméně na to, jakou výjimku vyhodíte, se žádná omezení nekladou, nakonec bude je zachytávat a ošetřovat opět váš kód.)
V případě úspěšné autentizace vrácí handler identitu, což je objekt implementující rozhraní Nette\Security\IIdentity a popisující aktuálního uživatele. Popis může obsahovat libovolné údaje, povinné je uživatelské jméno (což nemusí být nutně totéž, jako přihlašovací jméno) a role (o těch si povíme více v příštím dílu). K identitě se dostaneme přes getter getIdentity():
$user = Environment::getUser();
if ($user->isAuthenticated()) {
echo 'Prihlášen uživatel: ', $user->getIdentity()->getName();
} else {
echo 'Uživatel není přihlášen';
}
Odhlášení
Jak už jsem zmínil, uživatele odhlásí metoda signOut(). Při odhlášení se však nesmaže uživatelská identita, kterou máme i nadále k dispozici. Pokud bychom chtěli identitu explicitně smazat, odhlásíme uživatele voláním signOut(TRUE).
Kromě manuálního odhlášení nabízí Nette Framework i automatické odhlášení po uplynutí časového intervalu nebo zavření okna prohlížeče. K tomu slouží metoda setExpiration(), kterou volejte vždy před samotnou autentizací. Metoda setExpiration() jako parametr akceptuje relativní čas v sekundách nebo UNIX timestamp, v aktuální verzi frameworku je možné použít i velmi srozumitelný textový zápis. Druhý parametr stanoví, zda se má uživatel odhlásit při zavření okna prohlížeče:
// přihlášení vyprší po 30 minutách neaktivity nebo zavření okna prohlížeče
$user->setExpiration('+ 30 minutes');
// přihlášení vyprší po 2 dnech
$user->setExpiration('+ 2 days', FALSE);
Dokonce je možné zjistit, z jakého důvodu k poslednímu odhlášení došlo (viz metoda getSignOutReason).
Suma sumárum
Kompletní postup přihlašování uživatele pak vypadá asi takto:
require 'Nette/loader.php';
require 'MyAuthenticator.php';
// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use Nette\Environment, Nette\Security\AuthenticationException;
// přihlašovací údaje
$username = ...
$password = ...
$user = Environment::getUser();
// zaregistrujeme autentizační handler
$user->setAuthenticationHandler(new MyAuthenticator);
// nastavíme expiraci
$user->setExpiration('+ 30 minutes');
try {
// pokusíme se přihlásit uživatele...
$user->authenticate($username, $password);
// ...a v případě úspěchu presměrujeme na další stránku
Environment::getHttpResponse()->redirect('index.php');
} catch (AuthenticationException $e) {
echo 'Chyba: ', $e->getMessage();
}
Příště se podíváme na uživatelské role a ověřování uživatelských oprávnění.
Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Vyvíjí open-source knihovny Texy, dibi a Nette Framework a pravidelně pořádá školení pro tvůrce webových aplikací, které od podzimu 2009 nabídne kurz vývoje AJAXových aplikací.
Související odkazy
Školení Google+ pro firmy

- Jak využít Google+ pro firemní komunikaci a marketing.
- Čím se liší Google+ od Twitteru a Facebooku z pohledu firemního využití.
- Jak využít Google+ v souladu s pravidly užívání.
- Založení Google+ Page (Stránky) krok po kroku, včetně praktických tipů.
Detailní informace o školení Google+ »
Seriál Začínáme s Nette Framework
- Nette Framework: AJAX (dokončení)
- Nette Framework: Sessions
- Nette Framework: Přihlašování uživatelů
- Nette Framework: Ověřování oprávnění a role
- Nette Framework: Neprůstřelné formuláře
Přehled názorů
Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.