Session-Tracking ohne Cookies

(Auszug aus "Java und XSLT" von Eric M. Burke)

Session-Tracking ist in vielen Webanwendungen essentiell wichtig. Das HTTP-Protokoll ist von Natur aus verbindungslos. Das bedeutet, daß der Browser immer eine neue Verbindung zum Webserver herstellt, wenn der Nutzer einen Hyperlink betätigt oder ein Formular absendet. Nachdem die Anfrage gesendet und die Antwort empfangen wurde, wird die Verbindung zwischen Browser und Server erneut unterbrochen.

Dies stellt für Servlet-Autoren ein Problem dar. Obwohl der Browser und der Webserver keine beständige Verbindung zwischen den einzelnen Seitenaufrufen unterhalten, muß die Anwendung Zustandsinformationen für jeden Nutzer halten. Erst diese Zustandsinformationen ermöglichen Anwendungen wie z. B. Warenkörbe. Bei jeder Anfrage vom Browser muß das Servlet die Identität des Nutzers und seine Session-Informationen ermitteln.

Die Servlet-API zum Session-Tracking

Der traditionelle Ansatz, um in einem Servlet Session-Tracking zu ermöglichen, ist das Interface javax.servlet.http.HttpSession. Es erlaubt einer Webanwendung, Informationen über einen Nutzer zu speichern, die über mehrere Anfragen bestehen bleiben. Das Interface arbeitet mit Attributen, wobei jedes einen Namen und einen Wert besitzt. Der hier gezeigte Code ist Teil eines Servlets, das HttpSession verwendet:

public void doGet(HttpServletRequest req, HttpServletResponse res)
   throws ServletException, IOException {
 // hole eine Instanz von HttpSession für diesen Nutzer. Der Parameter "true"
 // besagt, daß das Objekt erzeugt werden soll, falls es nicht existiert.
 HttpSession session = req.getSession(true);
     
 // hole den Warenkorb dieses Nutzers
 Cart cart = (Cart) session.getAttribute("shoppingCart");
     
 if (cart == null) {
    cart = new Cart( );
    session.setAttribute("shoppingCart", cart);
 }

 ...
}

Die erste Zeile der Methode doGet( ) lokalisiert die Instanz von HttpSession, die dem Nutzer zugeordnet ist. Der Parameter true bewirkt, daß eine neue Session erzeugt wird, falls noch keine existiert. Mit der Methode getAttribute( ) der HttpSession kann nun ein Objekt des Typs Cart ermittelt werden.

Session-Tracking

Abbildung: Session-Tracking

Die HttpSession wird standardmäßig mit Browser-Cookies realisiert. Ein Cookie besteht aus einer zufällig generierten Sequenz von Zeichen, die auf dem Clientrechner gespeichert wird und einen Nutzer eindeutig identifiziert. Wenn der Browser eine Anfrage an das Servlet stellt, sucht dieses nach einem Cookie namens jsessionid und benutzt dessen Wert, um die passende Instanz von HttpSession zu ermitteln. Die obige Abbildung illustriert dieses Modell des Session-Trackings.

Cookies sind aber auch problematisch. Sie ermöglichen zwar eine einfache Implementierung von Session-Tracking, schüren aber auch die Bedenken von Nutzern, die ihre Surf-Gewohnheiten nicht überwacht wissen wollen. Deshalb wird die Unterstützung für Cookies von nicht wenigen Nutzern deaktiviert. In diesem Fall müssen Servlets eine andere Technik anwenden, um ein Session-Tracking zu ermöglichen.

Die standardisierte Servlet-API stellt einen anderen Mechanismus für den Fall bereit, wenn Cookies deaktiviert sind. Es handelt sich um das sogenannte URL-Rewriting, bei dem der Session-Identifier an die URL angehängt wird. Auf diese Weise wird immer dann, wenn ein Nutzer einen Hyperlink betätigt oder ein XHTML-Formular abschickt, der Session-Identifier zusammen mit der Anfrage verschickt. Das funktioniert allerdings nicht ohne einigen Programmieraufwand. Stellen Sie sich ein Szenario vor, in dem ein Servlet angesprochen wird, das eine XHTML-Seite mit dem folgenden Inhalt zurückliefert:

Hier klicken, um weiterzugehen:
<a href="/shopping/moveNext"/>weiter</a>

Das Session-Tracking kann so nicht funktionieren, da der Session-Identifier verlorengeht, wenn der Nutzer den Hyperlink betätigt. Der HTML-Code sollte folgendermaßen aussehen:

Hier klicken, um weiterzugehen:
<a href="/shopping/moveNext;jsessionid=0e394s8a576f67b38s7"/>weiter</a>

Wenn der Nutzer jetzt den Hyperlink betätigt, wird der Session-Identifier (jsessionid) als Teil der URL an das Servlet gesendet.

Der Wert von jsessionid darf natürlich nicht fest einprogrammiert werden. Er muß für jede Instanz von HttpSession dynamisch generiert werden, um es Hackern schwerer zu machen, an die Session-Identifier zu gelangen und sich als User auszugeben. (Sessions und die zugehörigen Identifier verfallen typischerweise nach 30 Minuten ohne Aktivität und müssen dann regeneriert werden.) Das bedeutet, daß der XHTML-Code nicht statisch sein darf, denn der Session-Identifier muß dynamisch für jeden Link und jede Aktion eines Formulars in den XHTML-Code eingefügt werden. HttpServletResponse stellt hierfür die Methode encodeURL( ) bereit:

String originalURL = "/shopping/moveNext";
String encodedURL = response.encodeURL(originalURL);

Die URL encodedURL wird jetzt durch die Session-ID erweitert, falls der Cookie jsessionid nicht gefunden wurde. Damit das Session-Tracking funktioniert, muß diese Technik für jeden Hyperlink und jede Formular-Aktion einer Website angewendet werden.

Session-Tracking mit Java und XSLT

Mit XSLT ist die Realisierung von Session-Tracking etwas komplizierter, da das Stylesheet die URL erzeugt und nicht das Servlet. Das Stylesheet könnte z.B. den folgenden Code enthalten:

<xsl:template match="cart">Weiter <a href="/shopping/moveNext"/>weiter</a> ...</xsl:template>

Wie schon erwähnt, muß die jsessionid an die URL angehängt werden. Dazu müssen die folgenden Schritte durchgeführt werden:

  1. Im Servlet wird bestimmt, ob Cookies aktiviert sind.
  2. Wenn sie deaktiviert sind, wird der Wert von jsessionid bestimmt.
  3. Der String ;jsessionid=XXXX wird als Parameter an das XSLT-Stylesheet übergeben, wobei XXXX für den Session-Identifier steht.
  4. Das Stylesheet hängt den Parameter mit der Session-ID an alle URLs in Hyperlinks und Formularaktionen an.

Wenn Cookies aktiviert sind, besteht kein Grund, manuelles Session-Tracking zu implementieren. Mit der Methode isRequestedSessionIdFromCookie( ) aus dem Interface javax.servlet.http.HttpServletRequest kann dies geprüft werden. Liefert diese Methode true, sind Cookies aktiviert, und die restlichen Schritte sind unnötig. Der Code aus dem folgenden Beispiel zeigt die Methode doGet( ) für ein Servlet, das Session-Tracking implementiert.

Beispiel: Beispiel zum Session-Tracking

public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
  try {
    // hole die aktuellen Einstellungen der Session
    HttpSession session = req.getSession(true);
    Cart cart = (Cart) session.getAttribute("shoppingCart");
    
    if (cart == null) {
      cart = new Cart( );
      session.setAttribute("shoppingCart", cart);
    }
      
    // erzeuge den DOM-Baum
    Document doc = CartDOMProducer.createDocument(cart);
      
    // bereite die XSLT-Transformation vor
    Transformer trans = StylesheetCache.newTransformer(
      this.xsltFileName);
      
    // ermögliche Session-Tracking ohne Cookies
    if (!req.isRequestedSessionIdFromCookie( )) {
      String sessionID = session.getId( );
      trans.setParameter("global.sessionID",
        ";jsessionid=" + sessionID);
    }
      
    // sende die Webseite zum Nutzer zurück
    res.setContentType("text/html");
    trans.transform(new javax.xml.transform.dom.DOMSource(doc),
      new StreamResult(res.getWriter( )));
      
  } catch (ParserConfigurationException pce) {
    throw new ServletException(pce);
  } catch (TransformerConfigurationException tce) {
    throw new ServletException(tce);
  } catch (TransformerException te) {
    throw new ServletException(te);
  }
}

Die erste entscheidene Zeile prüft, ob die Session ohne Cookie erzeugt wurde:

if (!req.isRequestedSessionIdFromCookie( )) {

Bei der ersten Anfrage existiert natürlich noch kein Cookie, da das Sevlet noch keines erzeugen konnte. Bei allen folgenden Anfragen fehlt der Cookie ebenfalls, wenn der Nutzer in seinem Browser Cookies deaktiviert hat. In beiden Fällen wird der Session-Identifier von der Instanz von HttpSession geliefert:

String sessionID = session.getId( );

Die Servlet-API sorgt auch dafür, daß ein zufälliger Session-Identifier erzeugt wird. Sie sind dafür verantwortlich, diesen Identifier zu speichern und als Parameter an das Stylesheet zu übergeben:

trans.setParameter("global.sessionID",
     ";jsessionid=" + sessionID);

Das Servlet stellt dem Session-Identifier auch den String ";jessionid=" voran. Dadurch wird dem XSLT-Stylesheet die Arbeit abgenommen, zu überprüfen, ob die Session-ID ein leerer String ist. Der Wert von global.sessionID kann jetzt an sämtliche URLs angehängt werden:

<a href="/beliebiger_link{$global.sessionID}">hier klicken</a>

Im Ergebnis wird bei aktivierten Cookies die URL nicht geändert. Ansonsten wird sie für das Session-Tracking modifiziert. Ein größeres XSLT-Stylesheet folgt im folgenden Beispiel.

Beispiel: XSLT-Stylesheet für Session-Tracking

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
   <!--
      *********************************************************************
      ** global.sessionID : Wird für das Umschreiben von URLs verwendet, um
      ** Session-Tracking ohne Cookies zu ermöglichen.
      ****************************************************************** -->
   <xsl:param name="global.sessionID"/>
   <!-- Dieses Stylesheet erzeugt XHTML -->
   <xsl:output method="xml" indent="yes" encoding="ISO-8859-1" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
   <!--
      *********************************************************************
      ** Dieses Template erzeugt das Gerüst des XHTML-Dokuments.
      ****************************************************************** -->
   <xsl:template match="/">
      <html>
         <head>
            <title>Online-Shop</title>
         </head>
         <body>
            <!-- Erzeuge auf dieser Seite ein Formular -->
            <form method="post" action="/chap8/shopping{$global.sessionID}">
               <h1>Online-Shop</h1>
               ...der Rest der Seite wurde ausgelassen
            </form>
         </body>
      </html>
   </xsl:template>
   <xsl:template match="cart">Betätigen Sie den Link, um weiterzugehen: <a href="/shopping/moveNext{$global.sessionID}?param=value"/>weiter</a>
      ...
   </xsl:template>
</xsl:stylesheet>

Das Stylesheet illustriert die drei Hauptkomponenten, die das Session-Tracking mit XSLT ermöglichen. Zuerst wird der Session-Identifier als Parameter an das Stylesheet übergeben:

<xsl:param name="global.sessionID"/>

Dann wird dieser Session-Identifier für die Aktion des Formulars verwendet:

<form method="post" action="/chap8/shopping{$global.sessionID}">

Schließlich wird er für alle Hyperlinks verwendet:

<a href="/shopping/moveNext{$global.sessionID}?param=value"/>weiter</a>

Der String ?param=value wurde angehängt, um zu verdeutlichen, daß die Anfrageparameter nach dem Session-Identifier angehängt werden. Die vollständige URL wird also wie folgt aussehen:

"http://localhost:8080/shopping/moveNext;jsessionid=298ulkj2348734jkj43?param=value"

Session-Tracking ist eine essentielle Technik, die mit dem hier vorgestellten Ansatz auch bei deaktivierten Browser-Cookies funktioniert. Sie sollten Ihre Webanwendung immer auch mit ausgeschalteten Cookies testen, um festzustellen, ob jede URL korrekt mit dem Session-Identifier erweitert wird.

   

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Java & XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Java und XSLT" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de