# Einführung

`Zend_Auth` bietet eine API für das Authentifizieren und enthält konkrete Authentifizierungs-Adapter für übliche Use-Case-Szenarien.

`Zend_Auth` behandelt nur die Authentifizierung und nicht die Authorisierung. Authentifizierung ist frei definiert als das Ermitteln, ob eine Entität aktuell das ist, was sie vorgibt zu sein (z.B. Identifizierung), basierend auf einem Set von Anmeldedaten. Authorisierung, der Prozess des Entscheidens, ob es einer Entität erlaubt wird, auf andere Entitäten Zugriff zu erhalten, oder um auf diesen Operationen durchzuführen, ist ausserhalb der Möglichkeit von `Zend_Auth`. Für weitere Informationen über Authorisierung und Zugriffskontrolle mit dem Zend Framework, sollte `Zend_Acl` angeschaut werden.

Die `Zend_Auth` Klasse implementiert das Singleton Pattern - nur eine Instanz der Klasse ist vorhanden - durch ihre statische Methode `getInstance()`. Das bedeutet, dass die Verwendung des Operators new und des Schlüsselworts clone mit der Klasse `Zend_Auth` nicht funktioniert; stattdessen muß `Zend_Auth::getInstance()` verwendet werden.

## Adapter

Ein `Zend_Auth`-Adapter wird verwendet, um sich gegenüber einem speziellen Typ von Authentifizierungsdiensten zu authentifizieren, wie LDAP, RDBMS, oder dateibasierenden Speichern. Verschiedene Adapter besitzen leicht unterschiedliche Optionen und Verhaltensweisen, aber einige grundlegende Dinge haben Authentifizierungsadapter gemeinsam. Zum Beispiel dass für die Authentifizierung Anmeldedaten akzeptiert werden (enthält auch vorgegebene Identitäten), dass Abfragen am Authentifizierungsservice durchgeführt werden, und dass Ergebnisse zurückgegeben werden, sind für `Zend_Auth`-Adapter gebräuchlich.

Jede `Zend_Auth`-Adapterklasse implementiert `Zend_Auth_Adapter_Interface`. Dieses Interface definiert eine Methode `authenticate()`, die eine Adapterklasse implementieren muß, um eine Authentifizierungsanfrage auszuführen. Jede Adapterklasse muß vorher vorbereitet werden, bevor `authenticate()` aufgerufen wird. Diese Vorbereitung des Adapters enthält das Setzen der Anmeldedaten (z.B. Benutzername und Passwort) und die Definition von Werten für adapterspezifische Konfigurationoptionen, wie Datenbankverbindungsdaten für einen Datenbank-Tabellen-Adapter.

Das folgende ist ein Beispiel eines Authentifierungs-Adapters der einen Benutzernamen und ein Passwort für die Authentifizierung benötigt. Andere Details, wie zum Beispiel der Authentifizierungs-Service abgefragt wird, werden der Kürze halber ausgelassen:

    class MyAuthAdapter implements Zend_Auth_Adapter_Interface
    {
        /**
         * Setzt Benutzername und Passwort für die Authentifizierung
         *
         * @return void
         */
        public function __construct($username, $password)
        {
            // ...
        }

        /**
         * Führt einen Authentifizierungs-Versuch durch
         *
         * @throws Zend_Auth_Adapter_Exception Wenn die Authentifizierung nicht
         *                                     durchgeführt wurde
         * @return Zend_Auth_Result
         */
        public function authenticate()
        {
            // ...
        }
    }

Wie im Docblock angegeben, muß `authenticate()` eine Instanz von `Zend_Auth_Result` (oder einer von `Zend_Auth_Result` abgeleiteten Klassen) zurückgeben. Wenn aus bestimmten Gründen eine Durchführung einer Authentifizierungsanfrage nicht möglich ist, sollte `authenticate()` eine Ausnahme werfen, die von `Zend_Auth_Adapter_Exception` abgeleitet ist.

## Ergebnisse

`Zend_Auth`-Adapter geben eine Instanz von `Zend_Auth_Result` mit Hilfe von `authenticate()` zurück, um die Ergebnisse des Authentifizierungsversuchs darzustellen. Adapter befüllen das Objekt `Zend_Auth_Result` bei der Erstellung, so dass die folgenden vier Methoden ein grundsätzliches Set von benutzerbezogenen Operationen bieten, die für die Ergebnisse von `Zend_Auth` Adapter üblich sind:

- `isValid()` - Gibt `TRUE` zurück, wenn und nur wenn das Ergebnis einen erfolgreichen Authentifizierungsversuch repräsentiert.

- `getCode()` - Gibt einen konstanten `Zend_Auth_Result`-Identifizierer zurück, damit der Typ des Authentifizierungsfehlers, oder des Erfolgs der stattgefunden hat, ermittelt werden kann. Das kann in Situationen verwendet werden, in denen der Entwickler die verschiedenen Ergebnistypen der Authentifizierung unterschiedlich behandeln will. Das erlaubt es dem Entwickler zum Beispiel detailierte Statistiken über die Authentifizierungsergebnisse zu erhalten. Eine andere Verwendung dieses Features ist es spezielle, benutzerdefinierte Nachrichten anzubieten, um Benutzern eine bessere Usability zu ermöglichen, einfach dadurch dass Entwickler dazu aufgerufen sind, die Risiken solche detaillierte Informationen Benutzern anzubieten, statt einer generellen Nachricht eines Authentifizierungsfehlers. Für weitere Informationen siehe die Notiz anbei.

- `getIdentity()` - Gibt die Identität des Authentifizierungsversuchs zurück

- `getMessages()` - Gibt ein Array von Nachrichten zurück nach einem fehlgeschlagenen Authentifizierungsversuch

Ein Entwickler möchte basierend auf dem Typ des Authentifizierungsergebnisses zu spezialisierteren Operationen verzweigen. Einige Operationen die für Entwickler nützlich sein können, sind zum Beispiel das Sperren von Konten nachdem zu oft ein falsches Passwort angegeben wurde, das Markieren von IP-Adressen, nachdem zuviele nicht existierende Identitäten angegeben wurden und das Anbieten von speziellen, benutzerdefinierten Nachrichten für Authentifizierungsergebnisse an den Benutzer. Die folgenden Ergebniscodes sind vorhanden:

    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

Das folgende Beispiel zeigt, wie ein Entwickler anhand des Ergebniscodes verzweigen könnte:

    // Innerhalb von AuthController / loginAction
    $result = $this->_auth->authenticate($adapter);

    switch ($result->getCode()) {

        case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND:
            /** Was wegen nicht existierender Identität machen **/
            break;

        case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
            /** Was wegen ungültigen Anmeldededaten machen **/
            break;

        case Zend_Auth_Result::SUCCESS:
            /** Was wegen erfolgreicher Authentifizierung machen **/
            break;

        default:
            /** Was wegen anderen Fehlern machen **/
            break;
    }

## Dauerhafte Identitäten

Eine Anfrage zu authentifizieren, die Anmeldedaten enthält ist per se nützlich, aber auch wichtig, um die Authentifizierungs-Identität bearbeiten zu können, ohne dass immer die Anmeldedaten bei jeder Anfrage vorhanden sein müssen.

Trotzdem ist HTTP ein statusloses Protokoll, und Techniken wie Cookies und Sessions wurden entwickelt um Stati über mehrere Anfragen hinweg in server-seitigen Web-Anwendungen zu erhalten.

### Normale Persistenz in PHP-Sessions

Standardmäßig bietet `Zend_Auth` dauerhafte Speicherung der Identität eines erfolgreichen Authentifizierungsversuches durch Verwendung der PHP-Session. Bei einem erfolgreichen Authentifizierungsversuch speichert `Zend_Auth::authenticate()` die Identität des Authentifizierungsergebnisses im persistenten Speicher. Solange die Konfiguration nicht verändert wird, verwendet `Zend_Auth` eine Speicherklasse die `Zend_Auth_Storage_Session` heißt und die im Gegenzug `Zend_Session` verwendet. Eine eigene Klasse kann stattdessen verwendet werden, indem ein Objekt an `Zend_Auth::setStorage()` übergeben wird, welches `Zend_Auth_Storage_Interface` implementiert.

Wenn das automatische persistente Speichern der Identität für einen bestimmten Anwendungsfall nicht anwendbar ist, können Entwickler trotzdem die `Zend_Auth` Klasse weiterhin verwenden, statt direkt eine Adapterklasse anzusprechen.

#### Den Namensraum der Session ändern

`Zend_Auth_Storage_Session` verwendet einen Session Namensraum von '`Zend_Auth`'. Dieser Namensraum kann überschrieben werden, indem ein anderer Wert an den Konstruktor von `Zend_Auth_Storage_Session` übergeben wird, und dieser Wert wird intern an den Konstruktor von `Zend_Session_Namespace` weitergereicht. Das sollte vor einem Versuch einer Authentifizierung stattfinden, da `Zend_Auth::authenticate()` die automatische Speicherung der Identität durchführt.

    // Eine Referenz zur Singleton Instanz von Zend_Auth speichern
    $auth = Zend_Auth::getInstance();

    // 'someNamespace' statt 'Zend_Auth' verwenden
    $auth->setStorage(new Zend_Auth_Storage_Session('someNamespace'));

    /**
     * @todo Den Auth Adapter $authAdapter erstellen
     */

    // Authentifizieren, das Ergebnis speichern, und die Identität bei Erfolg
    // persistent machen
    $result = $auth->authenticate($authAdapter);

### Eigene Speicher implementieren

Zeitweise wollen Entwickler einen anderen Speichermechanismus für Identitäten verwenden als es von `Zend_Auth_Storage_Session` angeboten wird. Für solche Fälle können Entwickler einfach `Zend_Auth_Storage_Interface` implementieren und eine Instanz der Klasse an `Zend_Auth::setStorage()` übergeben.

#### Eine eigene Speicherklasse verwenden

Um eine andere Speicherklasse für die Persistenz von Identitäten zu verwenden als sie durch `Zend_Auth_Storage_Session` angeboten wird, können Entwickler `Zend_Auth_Storage_Interface` implementieren:

    class MyStorage implements Zend_Auth_Storage_Interface
    {
        /**
         * Gibt true zurück wenn und nur wenn der Speicher leer ist
         *
         * @throws Zend_Auth_Storage_Exception Wenn es unmöglich ist festzustellen,
         *                                     ob der Speicher leer ist
         * @return boolean
         */
        public function isEmpty()
        {
            /**
             * @todo Implementierung
             */
        }

        /**
         * Gibt den Inhalt des Speichers zurück
         *
         * Das Verhalten ist undefiniert, wenn der Speicher leer ist.
         *
         * @throws Zend_Auth_Storage_Exception Wenn das Lesen vom Speicher
         *                                     unmöglich ist
         * @return mixed
         */
        public function read()
        {
            /**
             * @todo Implementierung
             */
        }

        /**
         * Schreibt $contents in den Speicher
         *
         * @param  mixed $contents
         * @throws Zend_Auth_Storage_Exception Wenn das Schreiben von $contents in
         *                                     den Speicher unmöglich ist
         * @return void
         */
        public function write($contents)
        {
            /**
             * @todo Implementierung
             */
        }

        /**
         * Löscht die Intalte vom Speicher
         *
         * @throws Zend_Auth_Storage_Exception Wenn das Löschen der Inhalte vom
         *                                     Speicher unmöglich ist
         * @return void
         */
        public function clear()
        {
            /**
             * @todo Implementierung
             */
        }

    }

Um diese selbstgeschriebene Speicherklasse zu verwenden wird, `Zend_Auth::setStorage()` aufgerufen, bevor eine Authentifizierungsanfrage stattfindet:

    // Zend_Auth anweisen, dass die selbstdefinierte Speicherklasse verwendet wird
    Zend_Auth::getInstance()->setStorage(new MyStorage());

    /**
     * @todo Den Auth Adapter $authAdapter erstellen
     */

    // Authentifizieren, das Ergebnis speichern, und die Identität bei Erfolg
    $result = Zend_Auth::getInstance()->authenticate($authAdapter);

## Verwendung

Es gibt zwei vorhandene Wege um `Zend_Auth`-Adapter zu verwenden:

1. Indirekt durch `Zend_Auth::authenticate()`

2. Direkt durch die `authenticate()` Methode des Adapters

Das folgende Beispiel zeigt, wie ein `Zend_Auth`-Adapter indirekt verwendet werden kann, durch die Verwendung der Klasse `Zend_Auth`:

    // Eine Referenz zur Singleton-Instanz von Zend_Auth erhalten
    $auth = Zend_Auth::getInstance();

    // Authentifizierungs Adapter erstellen
    $authAdapter = new MyAuthAdapter($username, $password);

    // Authentifizierungsversuch, das Ergebnis abspeichern
    $result = $auth->authenticate($authAdapter);

    if (!$result->isValid()) {
        // Authentifizierung fehlgeschlagen; die genauen Gründe ausgeben
        foreach ($result->getMessages() as $message) {
            echo "$message\n";
        }
    } else {
        // Authentifizierung erfolgreich; die Identität ($username) wird in
        // der Session gespeichert
        // $result->getIdentity() === $auth->getIdentity()
        // $result->getIdentity() === $username
    }

Sobald die Authentifizierung in einer Anfrage durchgeführt wurde, so wie im obigen Beispiel, ist es sehr einfach zu prüfen, ob eine erfolgreich authentifizierte Identität existiert:

    $auth = Zend_Auth::getInstance();
    if ($auth->hasIdentity()) {
        // Identität existiert; auslesen
        $identity = $auth->getIdentity();
    }

Um eine Identität vom persistenten Speicher zu entfernen, muß einfach die Methode `clearIdentity()` verwendet werden. Das würde typischerweise für die Implementierung einer "Abmelde"-Operation in einer Anwendung Verwendung finden.

    Zend_Auth::getInstance()->clearIdentity();

Wenn die automatische Verwendung von persistenten Speichern für einen bestimmten Verwendungszweck unangebracht ist, kann ein Entwickler einfach die Verwendung der Klasse `Zend_Auth` umgehen, und eine Adapterklasse direkt verwenden. Die direkte Verwendung einer Adapterklasse enthält das Konfigurieren und Vorbereiten eines Adapter-Objekts und den Aufruf dessen Methode `authenticate()`. Adapter-spezifische Details werden in der Dokumentation jedes Adapters besprochen. Das folgende Beispeil verwendet `MyAuthAdapter` direkt:

    // Den Authentifizierungsadapter erstellen
    $authAdapter = new MyAuthAdapter($username, $password);

    // Authentifizierungsversuch, speichere das Ergebnis
    $result = $authAdapter->authenticate();

    if (!$result->isValid()) {
        // Authentifizierung fehlgeschlagen; die genauen Gründe ausgeben
        foreach ($result->getMessages() as $message) {
            echo "$message\n";
        }
    } else {
        // Authentifizierung erfolgreich
        // $result->getIdentity() === $username
    }