Weiter zum Inhalt

Zend Framework und Authorisierung

Immer wieder gibt es die Anforderung, das bestimmte Seiten einer Webseite nur bestimmten Nutzern zugänglich sein sollen. Spätestens dann muss der Entwickler sich mit Themen wie Autorisierung, Authentifizierung, User-Management und ähnlichen auseinander setzen. Dieser Artikel kümmert sich erst einmal darum, wie mit Hilfe des Zend Frameworks geprüft werden kann, ob der User überhaupt der User ist (Authentifizierung).

Das Zend Framework bietet für diese Aufgabe die Klasse Zend_Auth. Aber wie wird diese sinnvoll verwendet? Dazu sollten wir uns, bevor wir mit dem Codieren beginnen, noch ein paar Gedanken zur Nutzerführung machen…

Wie ist normalerweise der Ablauf, wenn User auf zugriffsbeschränkte Seiten zugreifen wollen? Entweder, der Nutzer geht gezielt auf die Seite, er hat einen Bookmark oder er bekommt einen Link, zum Beispiel via Instant Messenger. Bei allen drei Möglichkeiten, die noch lange keine vollständige Aufzählung darstellen, erwartet der User auf bestimmten Seiten zu landen. Zuvor soll er sich aber bitte ausweisen.
Schön und gut. Aber wenn wir ehrlich sind, erwartet der User auch verschiedene Seiten nach dem erfolgreichen Login. Surft er die Seite direkt an, wird er sein Account oder irgendeine andere „Standardseite“ erwarten. Das ist also weniger problematisch. Hat sich der Nutzer aber einen Bookmark gesetzt, wird er das dazwischen geschaltete Login verkraften, aber danach will er auf der Seite landen, die er sich in seinem Browser gemerkt hat. Ähnliches erwartet der Webseitenbesucher auch, wenn er einen Link von einem Freund bekommt.

Wie es der Zufall will, unterstützt uns das Zend Framework in diesem Zusammenhang gewaltig. Denn es beherrst internes Routing by Design. Ruft unser Besucher die Seite mit einer URL, sagen wir www.example.com/user/statistics, auf, werden verschiedene Verarbeitungsschritte durchlaufen, bevor es letztendlich zum Ausführen der Action Statistics des Controllers User kommt. Und diese Verarbeitung können wir mit Hilfe von Plugins verändern. Dadurch bleibt die URL, welche ursprünglich aufgerufen wurde, unverändert. Wir können auch Fehlermeldungen und eine Wiedervorlage des Login-Formulars realisieren, ohne die URL der Zielseite irgendwie persistieren zu müssen.

Ein Plugin ist simpel aufgebaut. Es ist eine Klasse, die Zend_Controller_Plugin_Abstract beerbt. In dieser Klasse sind alle so genannten Hooks, also die Verarbeitungsschritte, die wir manipulieren können, leer implementiert. Wir brauchen nur die Methoden überschreiben, die wir auch benötigen. Welche das sind, schauen wir uns nun an.

Zuerst wollen wir ermitteln, ob der User vielleicht schon authentifiziert ist. Dafür bietet uns die Zend_Auth Klasse die Methode hasIdentity(). Die Identität wird bei erfolgreichen Login per Default in die Session geschrieben und ist nichts anderes als der Nutzername. Die Methode hasIdentity() prüft jedoch nur, ob eine Identität über die Zend_Auth Instanz verfügbar ist.
Diese Funktionalität machen wir uns jetzt im preDispatch-Hook zu Nutze. Der preDispatch wird vor dem eigentlichen Ermitteln des Controllers – dem Dispatching – durchgeführt. Mehr Infos finden sich in der ZF Dokumentation zum Dispatcher oder bei Thorsten Ruf.

Den Hook preDispatch zu nutzen ist denkbar einfach. Wir müssen einfach die gewünschte Funktionalität in der gleichnamigen Methode implementieren. Ich lege dafür im Verzeichnis library das Verzeichnis hl und darin das Verzeichnis Plugin an. So kann der Zend Loader später das Plugin mit dem Namen hl_Plugin_Authentication richtig lokalisieren. Das Plugin, welches eine einfache Klasse ist, spezialisiert die schon genannte Klasse Zend_Controller_Plugin_Abstract. Damit ergibt sich folgendes Grundgerüst:

class hl_Plugin_Authentication extends Zend_Controller_Plugin_Abstract {

 public function preDispatch (Zend_Controller_Request_Abstract $oRequest) {
 $oAuth = Zend_Auth::getInstance(); // Instanz holen

 if (!$oAuth->hasIdentity() && !in_array($sControllerName, array('login', 'index'))) {
 $oRequest->setModuleName('default')  // Modul-Name auf Default setzen
 ->setControllerName('login')  // Controller "Login" auswaehlen
 ->setActionName('index')  // im Login-Controller die Action "index" ansteuern
 ->setDispatched(true);   // und als fertig geroutet definieren
 }
 }
}

Dieser kleine Schnipsel Code sorgt dafür, das jede Anfrage auf die Action „index“ im Controller „login“ im Modul „default“ intern weiter geleitet wird. Davon bekommt der Nutzer allerdings nur mit, das er nicht auf der erwarteten Seite landet, sondern eine Login Maske ihn begrüßt.
An dieser Stelle muss natürlich auch berücksichtigt werden, dass das Routing nur geschehen soll, wenn der Nutzer nicht sowieso schon die Action ansteuert, die das Login anbietet. Daher wird nach der Prüfung, ob der Nutzer schon eingeloggt ist, geprüft, ob nicht bereits der Login Controller angesteuert wird. Und da ich gern die Startseite ebenfalls ohne Loginseite zugänglich machen möchte, prüfe ich noch auf den Index-Controller.

Ok, wenn wir das Plugin nun am FrontendController mittels

// die aktuelle Instanz des Front-Controllers holen
$frontController = Zend_Controller_Front::getInstance();

// und das Plugin hinzufuegen...
$frontController->registerPlugin (new hl_Plugin_Authentication());

anmelden, werden wir sehen, das alle Anfragen, außer auf Login und Index Controller, immer auf dem Controller und der Action landen, die wir oben definiert haben. Ein guter Platz für das Registrieren des Plugins könnte die index.php oder Ihre Bootstrap-Datei, so vorhanden, sein.

Da es auf die Dauer langweilig wird, immer nur ein Login-Fenster vorgesetzt zu bekommen, werden wir uns jetzt mal um das eigentliche Authentifizieren kümmern. Dafür werden wir das Plugin erweitern. Denn es ist zwar schön, das wir die URL immer parat haben, aber dadurch haben wir auch keinen zentralen Punkt, wo sonst eine Authentifizierung stattfinden könnte. Und wer will schon Code kopieren…

Um die Authentifzierung durchführen zu können benötigen wir natürlich erst einmal Logindaten. Da dies keine Zauberei ist, gehe ich einmal davon aus, das es ein Login-Formular gibt. In diesem Formular kann der Nutzer sein Login und sein Passwort eingeben. Diese Werte erwarte ich in den folgenden Beispielen in den Parametern ‚hl_login_user‘ und ‚hl_login_password‘. Natürlich können die Werte auch andere Namen tragen, es muss dann eben einfach beim Nachimplementieren berücksichtigt werden…

Wie zum Teufel wird denn jetzt aber geprüft? Nun, die Prüfung führen wir einfach vor dem eigentlichen internen Routing durch. Dafür bietet das Zend Framework den Hook routeStartup an. Die gleichnamige Methode bekommt zudem wieder ein Request-Objekt übergeben, welches die etwaigen Werte vom Login mit sich trägt. Das sieht dann ungefähr so aus:

public function routeStartup (Zend_Controller_Request_Abstract $oRequest) {
 if (!is_null($oRequest->getParam("hl_login_send"))) {
 $oAuth = Zend_Auth::getInstance();

 $sUser = $oRequest->getParam('hl_login_user');
 $sPassword = $oRequest->getParam('hl_login_pass');

 // get config for ldap...
 $sIniPath = Zend_Registry::get('ini_path');
 $oConfig = new Zend_Config_Ini ($sIniPath . "config.ini", "ldap");

 $aOptions = $oConfig->ldap->auth->toArray();

 $oLdapAdapter = new Zend_Auth_Adapter_Ldap();
 $oLdapAdapter->setOptions($aOptions);
 $oLdapAdapter->setUsername($sUser)
 ->setPassword($sPassword);

 $oAuth->authenticate($oLdapAdapter);
 }
}

Ich nutze hier den seit der Version 1.5 enthaltenen Zend_Auth_Adapter_Ldap, welcher es ermöglicht, Authentifizierungen auch gegen ein LDAP (z.B. OpenLDAP, Active Directory, eDirectory) durchführen zu können. Die benötigte Konfiguration steht in einer INI-File, welche über Zend_Config_Ini geladen wird. Der Pfad zu den INI Dateien wird von mir in der statischen Registry unter dem Wert ‚ini_path‘ gespeichert.
Das ist eigentlich schon der gesamte Zauber des puren Authentifizierens. Wenn der Button mit dem Namen „hl_login_send“ gedrückt wurde, werden die Werte für „hl_login_user“ und „hl_login_pass“ gelesen. Diese Werte gebe ich dem Authentifikationsadapter, hier der Adapter für LDAP, zum weiteren Verwerten und beauftrage mit der Methode authenticate() von Zend_Auth zu prüfen, ob die Logindaten richtig sind. Bei Erfolg setzt Zend_Auth die Identität des Nutzers in die Session und wir können mit hasIdentity() und getIdentity() darauf zugreifen.

Da wir die Login-Prüfung vor dem Routing und Dispatching durchführen greift unser internes Routing nach dem Login. Wenn der User sich also mit den richtigen Werten angemeldet hat landet er, wie eingangs besprochen, auf der erwarteten Seite. Sollte er sich beim Login vertippt haben bekommt er die Login-Seite wieder angezeigt.

Damit haben Sie bereits eine recht effektive Authentifizierung realisiert. Kleinere Anwendungen können Sie nun schnell nur bestimmten Nutzern zugänglich machen. Für größere Anwendungen werden Sie sicherlich ein granulareres Rechtemanagement implementieren wollen. Denn nur selten ist die Anforderung, einfach zwischen „darf alles“ und „darf nichts“ zu unterscheiden. Aber auch dafür bietet das Zend Framework bereits tatkräftige Unterstützung. Das Stichwort lautet

{ 5 } Comments

  1. michael | 8. April 2009 um 16:02 | Permalink

    super einstieg danke,

    aber es muss heissen

    public function preDispatch() …

    im ersten code sample!

  2. Holger Librenz | 8. April 2009 um 18:00 | Permalink

    Danke für das Feedback!
    Das vermisste „function“ hab ich sofort aus dem Urlaub zurück gepfiffen und wieder an den freien Platz kommandiert.

  3. Webagentur | 11. April 2009 um 12:30 | Permalink

    Ich bin mich jetzt auch gerade im Zend Framework am einarbeiten und wollte für unseren Onlineshop die Zend Authentifizierung mal testweise einsetzen. Dieses kleine Tutorial hatte mir sehr geholfen, danke…

  4. Klaus Schade | 23. April 2009 um 12:05 | Permalink

    Vielen Dank für diesen guten Artikel, er hat mir sehr beim Einstieg in das Zend Framework geholfen.
    Es muss Authentifizierung heißen (http://de.wikipedia.org/wiki/Authentifizierung).
    Autorisierung (ohne ‚h‘) wird von Zend_Acl unterstützt.

    Thorsten Ruf (http://nethands.de/download/zenddispatch.pdf) hat eine Grafik zur Erklärung der Abläufe im
    Zend_Controller_Front::dispatch() erstellt. Finde ich sehr hilfreich.

  5. Holger | 6. Mai 2009 um 18:34 | Permalink

    Danke für das Feedback. Den Link habe ich mal direkt mit in den Text gepackt und auch der kleine Fehler wurde ausgemerzt!

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close