AngularJS in WARs – Das Problem des Session-Timeouts

Artikel in english ⤴︎

AngularJS ist ein hervorragendes Framework für moderne Webanwendungen. Java EE bietet vielfältige und komfortable Möglichkeiten für sichere und skalierbare Serveranwendungen. Die Kombination von beiden ist recht einfach: In das Web-Archiv (WAR) kommen alle HTML und JavaScript Dateien, der Zugriff auf den Server geschieht per JAX-RS.

Auch der Zugriffsschutz lässt sich mit Bordmitteln einfach erledigen. Über Form-based Authentication bekommt der Anwender ein Formular präsentiert, in das er Name und Passwort eingibt. Erst dann hat er Zugriff auf die Web-Seiten und auch auf das Servlet, welches von AngularJS angesprochen wird.

Damit ist dann alles kein Problem mehr, oder? Fast. Was nicht automatisch passiert, ist die Behandlung von Session-Timeouts. Läuft die Sitzung ab, bekommt ein Anwender wieder das Formular präsentiert, um sich zu authentifizieren. Das ist für den Anwender ok, für die AngularJS-Anwendung, die asynchron mit dem Server kommuniziert, ist es aber verwirrend. Wir zeigen hier eine Lösung für das Problem.

Diese Lösung ist ausführlich in unserem Buch beschrieben. Wer daran Interesse hat, folgt diesem Link:
https://itunes.apple.com/de/book/rich-web-apps-mit-angularjs/id847457516

Der Code dazu findet sich auf GitHub:
https://github.com/akquinet/dailyplanner-angularjs

Die Idee besteht darin, bei jedem Serveraufruf zu prüfen, ob die Sitzung ausgelaufen ist. Falls dem so ist, lenken wir den Browser auf die Anmeldeseite um. Um das nicht bei jedem Aufruf händisch zu tun, melden wir unseren Interceptor mit dem Namen httpInterceptor am $httpProvider bei der Konfiguration der Anwendung an:

.config([
    "$httpProvider",
    function ($httpProvider) {
        $httpProvider
            .interceptors.push("httpInterceptor");
    }
]);

Die nächste Frage ist nun, wie kann die Anwendung erkennen, dass die Sitzung abgelaufen ist? Der Server schickt in dem Fall die Anmeldeseite. Um nun die Anmeldeseite von anderen Fehlerseiten des Servers unterscheiden zu können, haben wir eine zusätzliche Metainformation in den HTML-Kopf der Anmeldeseite eingefügt:

  <meta name="unauthorized" content="true">

Unser Interceptor prüft nun bei jeder Antwort, ob diese eine HTML-Seite ist und, falls ja, ob die Metainformation enthalten ist. Treffen alle Bedingungen zu, wird der Anwender auf die Anmeldung umgeleitet. Zusätzlich teilt der Interceptor dem HTTP-Subsystem mit, dass der Aufruf fehlgeschlagen ist und die Antwort zu verwerfen ist. Hier ist der Code:

.factory("httpInterceptor", ["$q", "$window", "$log",
  function ($q, $window, $log) {
    return {
     "response": function (response) {
        var responseHeaders;
        responseHeaders = response.headers();
        if (   responseHeaders["content-type"]
                 .indexOf("text/html") !== -1
               && response.data
               && response.data
                   .indexOf('<meta name="unauthorized" content="true">') 
                      !== -1) {
          $window.location.reload();
          return $q.reject(response);
        }
        return response;
      }
    };
 }
])

Diese Lösung funktioniert zuverlässig. Sie hat aber zwei Nachteile:

  • Es ist stets sicher zu stellen, dass die Metainformation in der Anmeldeseite richtig gesetzt wird und auch nur da. Wir benutzen ausführliche Kommentare in dem HTML-Code, damit Entwickler es nicht vergessen.
  • Bei jeder HTML-Seite, welche die AngularJS-Anwendung lädt, wird die ganze Seite nach der Metainformation durchsucht. Bei unseren Anwendungen ist dies kein Problem, da der Server fast ausschließlich JSON-Fragmente schickt. Werden aber viele HTML-Fragmente ausgetauscht, kann der Mehraufwand durch die Suche signifikant werden.

An besseren Lösungen sind wir natürlich immer interessiert.

🙂

Veröffentlicht in Alle