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 (17 dílů)

  1. Nette Framework: zvyšte svoji produktivitu 10.3.2009
  2. Nette Framework: Odvšivujeme 17.3.2009
  3. Nette Framework: MVC & MVP 24.3.2009
  4. Nette Framework: Refactoring 31.3.2009
  5. Nette Framework: Chytré šablony 7.4.2009
  6. Nette Framework: adresářová struktura aplikace 14.4.2009
  7. Nette Framework: AJAX 21.4.2009
  8. Nette Framework: AJAX (pokračování) 28.4.2009
  9. Nette Framework: AJAX (dokončení) 5.5.2009
  10. Nette Framework: Sessions 12.5.2009
  11. Nette Framework: Přihlašování uživatelů 19.5.2009
  12. Nette Framework: Ověřování oprávnění a role 26.5.2009
  13. Nette Framework: Neprůstřelné formuláře 2.6.2009
  14. Nette Framework: Neprůstřelné formuláře II 9.6.2009
  15. Nette Framework: Neprůstřelné formuláře III 16.6.2009
  16. Nette Framework: Cache 23.6.2009
  17. Nette Framework: Co se do seriálu nevešlo? 30.6.2009

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 NetteWebUser. Ta je, stejně jako v případě NetteWebSes­sion, 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í NetteSecurityI­Authenticator. 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 NetteObject, NetteSecurityIAuthenticator, NetteSecurityAuthenticationException, NetteSecurityIdentity;

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 NetteSecurityAu­thenticationEx­ception 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í NetteSecurityI­Identity 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 NetteEnvironment, NetteSecurityAuthenticationException;

// 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í.

David Grudl školí, je autorem PHP knihoven Nette Framework, databázové vrstvy dibi a formátovače HTML kódu Texy!.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Zdroj: https://www.zdrojak.cz/?p=3011