Rozdział 3. Zend_Auth

Spis treści

3.1. Wprowadzenie
3.1.1. Adaptery
3.1.2. Resultat
3.1.3. Trwałość uwierzytelnionej tożsamości
3.1.4. Użycie Zend_Auth
3.2. Uwierzytelnianie w oparciu o tabelę bazy danych
3.2.1. Wprowadzenie
3.2.2. Zaawansowane użycie: Stałe przechowywanie obiektu DbTable Result
3.3. Uwierzytelnianie Digest
3.3.1. Wprowadzenie
3.3.2. Parametry
3.3.3. Tożsamość
3.4. HTTP Authentication Adapter
3.4.1. Introduction
3.4.2. Design Overview
3.4.3. Configuration Options
3.4.4. Resolvers
3.4.5. Basic Usage

3.1. Wprowadzenie

Zend_Auth zapewnia API do uwierzytelniania oraz zawiera konkretne adaptery uwierzytelniania dla najczęstszych przypadków użycia.

Komponent Zend_Auth jest związany tylko z uwierzytelnianiem, a nie z autoryzacją. Uwierzytelnianie luźno definiujemy jako określanie w oparciu o pewien zestaw danych tego, czy dana jednostka jest tym na co wygląda (np. identyfikacja). Autoryzacja, proces decydowania o tym, czy zezwolić danej jednostce na dostęp lub przeprowadzanie operacji na innych jednostkach, jest poza polem działania Zend_Auth. Aby uzyskać więcej informacji o autoryzacji i kontroli dostępu za pomocą Zend Framework, proszę zobacz Zend_Acl.

[Notatka] Notatka

Klasa Zend_Auth implementuje wzorzec singletona, czyli dostępna jest tylko jej jedna instancja - za pomocą statycznej metody getInstance(). Oznacza to, że użycie operatorów new oraz clone nie będzie możliwe z klasą Zend_Auth; zamiast nich użyj metody Zend_Auth::getInstance().

3.1.1. Adaptery

Adapter Zend_Auth jest używany do uwierzytelniania na podstawie serwisu konkretnego typu, takiego jak LDAP, RDBMS, lub system plików. Różne adaptery mogą mieć różne opcje i mogą inaczej się zachowywać, ale niektóre podstawowe funkcjonalności są wspólne dla wszystkich adapterów. Na przykład akceptowanie danych uwierzytelniania, przeprowadzanie zapytań do serwisu uwierzytelniania i zwracanie rezultatów są wspólne dla adapterów Zend_Auth.

Każda klasa adaptera Zend_Auth implementuje interfejs Zend_Auth_Adapter_Interface. Ten interfejs definiuje jedną metodę, authenticate(), którą klasa adaptera musi implementować dla zastosowań przeprowadzania zapytania uwierzytelniania. Każda klasa adaptera musi być przygotowana przed wywołaniem metody authenticate(). Przygotowanie takiego adaptera obejmuje ustawienie danych uwierzytelniania (np. nazwy użytkownika i hasła) oraz zdefiniowanie wartości dla specyficznych opcji adaptera, na przykład ustawienie połączenia do bazy danych dla adaptera tabeli bazy danych.

Poniżej jest przykładowy adapter uwierzytelniania, który do przeprowadzenia procesu wymaga ustawionej nazwy użytkownika oraz hasła. Inne szczegóły, takie jak sposób przeprowadzania zapytania uwierzytelniającego, zostały pominięte w celu zwiększenia czytelności:

<?php
require_once 'Zend/Auth/Adapter/Interface.php';

class MyAuthAdapter implements Zend_Auth_Adapter_Interface
{
    /**
     * Ustawia nazwę użytkownika oraz hasła dla uwierzytelniania
     *
     * @return void
     */
    public function __construct($username, $password)
    {
        // ...
    }

    /**
     * Przeprowadza próbę uwierzytelniania
     *
     * @throws Zend_Auth_Adapter_Exception Jeśli uwierzytelnianie nie może być przeprowadzone
     * @return Zend_Auth_Result
     */
    public function authenticate()
    {
        // ...
    }
}

Jak pokazano w bloku dokumentacyjnym , metoda authenticate() musi zwracać instancję Zend_Auth_Result (lub instancję klasy rozszerzającej Zend_Auth_Result). Jeśli z jakiegoś powodu przeprowadzenie zapytania uwierzytelniającego jest niemożliwe, metoda authenticate() powinna wyrzucić wyjątek rozszerzający Zend_Auth_Adapter_Exception.

3.1.2. Resultat

Adaptery Zend_Auth zwracają instancję Zend_Auth_Result za pomocą metody authenticate() w celu przekazania rezultatu próby uwierzytelniania. Adaptery wypełniają obiekt Zend_Auth_Result podczas konstrukcji, dlatego poniższe trzy metody zapewniają podstawowy zestaw operacji, które są wspólne dla rezultatów adapterów Zend_Auth:

  • isValid() - zwraca logiczną wartość true tylko wtedy, gdy rezultat reprezentuje udaną próbę uwierzytelniania.

  • getCode() - zwraca identyfikator w postaci stałej klasy Zend_Auth_Result dla określenia powodu nieudanego uwierzytelniania lub sprawdzenia czy uwierzytelnianie się udało. Metoda może być użyta w sytuacjach gdy programista chce rozróżnić poszczególne typy wyników uwierzytelniania. Pozwala to na przykład programiście na zarządzanie szczegółowymi statystykami na temat wyników uwierzytelniania. Innym przykładem użycia tej funkcjonalności może być potrzeba zapewnienia wiadomości informujących użytkownika o przebiegu uwierzytelniania, ale jednak zalecane jest rozważenie ryzyka jakie zachodzi przy przekazywaniu użytkownikowi takich szczegółowych informacji, zamiast podstawowej informacji o błędzie. Aby uzyskać więcej informacji, zobacz poniżej.

  • getIdentity() - zwraca tożsamość próby uwierzytelniania

  • getMessages() - zwraca tablicę wiadomości odnoszących się do nieudanej próby uwierzytelniania

Programista może chcieć przeprowadzić jakieś specyficzne akcje zależne od typu wyniku uwierzytelniania. Przykładami operacji, które programiści mogą uznać za użyteczne, mogą być: blokowanie kont po zbyt dużej ilości nieudanych próbach logowania, zapiywanie adresów IP po wpisaniu przez użytkownika nieistnięjącej nazwy tożsamości czy zapewnienie własnych zdefiniowanych komunikatów po próbie uwierzytelniania. Dostępne są takie kody wyników:

Zend_Auth_Result::SUCCESS
Zend_Auth_Result::FAILURE
Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND
Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS
Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID
Zend_Auth_Result::FAILURE_UNCATEGORIZED

Poniższy przykład pokazuje w jaki sposób programista może obsłużyć to kodzie:

<?php
// wewnątrz akcji loginAction kontrolera AuthController
$result = $this->_auth->authenticate($adapter);

switch ($result->getCode()) {

    case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND:
        /** obsługujemy nieistniejącą tożsamość **/
        break;

    case Zend_Auth_Result::FAILURE_INVALID_CREDENTIAL:
        /** obsługujemy nieprawidłowe hasło **/
        break;

    case Zend_Auth_Result::SUCCESS:
        /** obsługujemy udane uwierzytelnianie **/
        break;

    default:
        /** obsługujemy inne błędy **/
        break;
}

3.1.3. Trwałość uwierzytelnionej tożsamości

Uwierzytelnianie żądania, które zawiera dane uwierzytelniające jest samo w sobie użyteczne, ale ważna jest także obsługa uwierzytelnionej tożsamości bez konieczności dołączania danych uwierzytelniających do każdego żądania.

HTTP jest protokołem niezachowującym stanu pomiędzy żądaniami, a techniki takie jak pliki cookie oraz sesje zostały stworzone w celu ułatwienia zarządzania stanem pomiędzy żądaniami w aplikacjach serwerowych.

3.1.3.1. Domyślne przechowywanie w sesji PHP

Domyślnie Zend_Auth zapewnia trwały pojemnik do przechowywania tożsamości pochodzącej z udanej próby uwierzytelniania używając sesji PHP. Po udanej próbie uwierzytelniania, metoda Zend_Auth::authenticate() przechowuje wtrwałym pojemniku tożsamość pochodzącą z wyniku uwierzytelniania. Jeśli nie skonfigurujemy tego inaczej, klasa Zend_Auth użyje klasy pojemnika o nazwie Zend_Auth_Storage_Session, który używa klasy Zend_Session. Zamiast tego za pomocą metody Zend_Auth::setStorage() może być ustawiona własna klasa implementująca interfejs Zend_Auth_Storage_Interface.

[Notatka] Notatka

Jeśli automatyczne przechowywanie tożsamości w trwałym pojemniku nie jest odpowiednie dla konkretnego przypadku użycia, to programiści mogą obyć się bez klasy Zend_Auth, a zamiast niej użyć bezpośrednio klasy adaptera.

Przykład 3.1. Modyfikowanie przestrzeni nazw sesji

Zend_Auth_Storage_Session używa przestrzeni nazw sesji o nazwie 'Zend_Auth'. Ta przestrzeń nazw może być nadpisana przez przekazanie innej wartości do konstruktora klasy Zend_Auth_Storage_Session, a ta wartość wewnętrznie jest przekazywana do konstruktora klasy Zend_Session_Namespace. Powinno to nastąpić zanim przeprowadzone zostanie uwierzytelnianie, ponieważ metoda Zend_Auth::authenticate() automatycznie zapisuje dane tożsamości.

<?php
// Zapisujemy referencję do pojedynczej instancji Zend_Auth
require_once 'Zend/Auth.php';
$auth = Zend_Auth::getInstance();

// Używamy przestrzeni nazw 'someNamespace' zamiast 'Zend_Auth'
require_once 'Zend/Auth/Storage/Session.php';
$auth->setStorage(new Zend_Auth_Storage_Session('someNamespace'));

/**
 * @todo Ustawić adapter uwierzytelniania, $authAdapter
 */

// Uwierzytelniamy, zapisując wynik i przechowując tożsamość po udanym uwierzytelnieniu
$result = $auth->authenticate($authAdapter);

3.1.3.2. Implementacja własnego pojemnika

Czasem programiści mogą potrzebować użyć innego sposobu trwałego przechowywania tożsamości niż ten zapewniony przez Zend_Auth_Storage_Session. W takich przypadkach programiści mogą po prostu zaimplementować interfejs Zend_Auth_Storage_Interface i przekazać instancję klasy do metody Zend_Auth::setStorage().

Przykład 3.2. Użycie własnej klasy do przechowywania tożsamości

W celu użycia klasy trwale przechowującej tożsamość innej niż Zend_Auth_Storage_Session, programista implementuje interfejs Zend_Auth_Storage_Interface:

<?php
require_once 'Zend/Auth/Storage/Interface.php';

class MyStorage implements Zend_Auth_Storage_Interface
{
    /**
     * Zwraca wartość logiczną true tylko wtedy gdy pojemnik jest pusty
     *
     * @throws Zend_Auth_Storage_Exception Jeśli okreslenie czy pojemnik jest pusty jest niemożliwe
     * @return boolean
     */
    public function isEmpty()
    {
        /**
         * @todo implementacja
         */
    }

    /**
     * Zwraca zawartość pojemnika
     *
     * Zachowanie jest nieokreślone w przypadku gdy pojemnik jest pusty.
     *
     * @throws Zend_Auth_Storage_Exception Jeśli odczyt zawartości pojemnika jest niemożliwy
     * @return mixed
     */
    public function read()
    {
        /**
         * @todo implementacja
         */
    }

    /**
     * Zapisuje zawartość $contents w pojemniku
     *
     * @param  mixed $contents
     * @throws Zend_Auth_Storage_Exception Jeśli zapisanie zawartości $contents do pojemnika jest niemożliwe
     * @return void
     */
    public function write($contents)
    {
        /**
         * @todo implementacja
         */
    }

    /**
     * Czyści zawartość pojemnika
     *
     * @throws Zend_Auth_Storage_Exception Jeśli wyczyszczenie zawartości pojemnika jest niemożliwe
     * @return void
     */
    public function clear()
    {
        /**
         * @todo implementacja
         */
    }

}

W celu użycia własnej klasy pojemnika, wywołaj metodę Zend_Auth::setStorage() przed przeprowadzeniem zapytania uwierzytelniającego:

<?php
// Instruujemy klasę Zend_Auth aby użyła niestandardowej klasy pojemnika
Zend_Auth::getInstance()->setStorage(new MyStorage());

/**
 * @todo Ustawić adapter uwierzytelniania, $authAdapter
 */

// Uwierzytelniamy, zapisując wynik i przechowując tożsamość po udanym uwierzytelnieniu
$result = Zend_Auth::getInstance()->authenticate($authAdapter);

3.1.4. Użycie Zend_Auth

Są dwa możliwe sposoby użycia adapterów Zend_Auth:

  1. pośrednio, za pomocą metody Zend_Auth::authenticate()

  2. bezpośrednio, za pomocą metody authenticate() adaptera

Poniższy przykład pokazuje jak użyć adaptera Zend_Auth pośrednio, poprzez użycie klasy Zend_Auth:

<?php
// Pobieramy instancję Zend_Auth
require_once 'Zend/Auth.php';
$auth = Zend_Auth::getInstance();

// Ustawiamy adapter uwierzytelniania
$authAdapter = new MyAuthAdapter($username, $password);

// Przeprowadzamy uwierzytelnianie, zapisując rezultat
$result = $auth->authenticate($authAdapter);

if (!$result->isValid()) {
    // Uwierzytelnianie nieudane; wyświetlamy powody
    foreach ($result->getMessages() as $message) {
        echo "$message\n";
    }
} else {
    // Uwierzytelnianie udane; tożsamość ($username) jest zapisana w sesji
    // $result->getIdentity() === $auth->getIdentity()
    // $result->getIdentity() === $username
}

Jeśli uwierzytelnianie zostało przeprowadzone w żądaniu tak jak w powyższym przykładzie, prostą sprawą jest sprawdzenie czy istnieje pomyślnie uwierzytelniona tożsamość:

<?php
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
    // Tożsamość istnieje; pobieramy ją
    $identity = $auth->getIdentity();
}

Aby usunąć tożsamość z trwałego pojemnika, użyj po prostu metody clearIdentity(). Typowo może być to użyte do implementacji w aplikacji operacji wylogowania:

<?php
Zend_Auth::getInstance()->clearIdentity();

Gdy automatyczne użycie trwałego pojemnika jest nieodpowiednie w konkretnym przypadku, programista może w prostu sposób ominąć użycie klasy Zend_Auth, używając bezpośrednio klasy adaptera. Bezpośrednie użycie klasy adaptera powoduje skonfigurowanie i przygotowanie obiektu adaptera, a następnie wywołanie metody authenticate(). Szczegóły specyficzne dla adaptera są opisane w dokumentacji dla każdego z adapterów. Poniższy przykład bezpośrednio używa MyAuthAdapter:

<?php
// Ustawiamy adapter uwierzytelniania
$authAdapter = new MyAuthAdapter($username, $password);

// Przeprowadzamy uwierzytelnianie, zapisując rezultat
$result = $authAdapter->authenticate();

if (!$result->isValid()) {
    // Uwierzytelnianie nieudane; wyświetlamy powody
    foreach ($result->getMessages() as $message) {
        echo "$message\n";
    }
} else {
    // Uwierzytelnianie udane
    // $result->getIdentity() === $username
}