Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 17 Servlets und Java Server Pages
gp 17.1 Dynamische Web-Seiten und Servlets
gp 17.1.1 Was sind Servlets?
gp 17.1.2 Was sind Java Server Pages?
gp 17.1.3 Vorteil von JSP/Servlets gegenüber CGI-Programmen
gp 17.2 Vom Client zum Server und wieder zurück
gp 17.2.1 Der bittende Client
gp 17.2.2 Was erzeugt ein Web-Server für eine Antwort?
gp 17.2.3 Wer oder was ist MIME?
gp 17.3 Servlets und Java Server Pages entwickeln und testen
gp 17.3.1 Servlet-Container
gp 17.3.2 Web-Server mit Servlet-Funktionalität
gp 17.3.3 Tomcat
gp 17.4 Java Server Pages in Tomcat und Eclipse
gp 17.4.1 Erster Ablageort für eigene JSP-Seiten
gp 17.4.2 Das Sysdeo-Plugin
gp 17.5 Skript-Elemente
gp 17.5.1 Scriptlets
gp 17.5.2 Ausdrücke
gp 17.5.3 Deklarationen
gp 17.5.4 Kommentare und Quoting
gp 17.6 Web-Applikationen
gp 17.7 Implizite Objekte
gp 17.8 Entsprechende XML-Tags
gp 17.9 Was der Browser mit auf den Weg gibt - HttpServletRequest
gp 17.9.1 Verarbeiten der Header
gp 17.9.2 Hilfsfunktion im Umgang mit Headern
gp 17.9.3 Übersicht der Browser-Header
gp 17.10 Formulardaten
gp 17.11 Das HttpServletResponse-Objekt
gp 17.11.1 Automatisches Neuladen
gp 17.11.2 Seiten umlenken
gp 17.12 JSP-Direktiven
gp 17.12.1 page-Direktiven im Überblick
gp 17.12.2 include-Direktive
gp 17.13 Aktionen
gp 17.13.1 Aktion include
gp 17.13.2 Aktion forward
gp 17.13.3 Aktion plugin
gp 17.14 Beans
gp 17.14.1 Beans in JSP-Seiten anlegen, Attribute setzen und erfragen
gp 17.14.2 Der schnelle Zugriff auf Parameter
gp 17.15 Kleine Kekse: die Klasse Cookies
gp 17.15.1 Cookies erzeugen und setzen
gp 17.15.2 Cookies vom Servlet einlesen
gp 17.15.3 Kleine Helfer für Cookies
gp 17.15.4 Cookie-Status ändern
gp 17.15.5 Langlebige Cookies
gp 17.15.6 Ein Warenkorbsystem
gp 17.16 Sitzungsverfolgung (Session Tracking)
gp 17.16.1 Das mit einer Sitzung verbundene Objekt HttpSession
gp 17.16.2 Werte mit einer Sitzung assoziieren und auslesen
gp 17.16.3 URL-Rewriting
gp 17.16.4 Zusätzliche Informationen
gp 17.17 Tag-Libraries
gp 17.17.1 Standard Tag Library (JSTL)
gp 17.18 Servlets
gp 17.18.1 Servlets compilieren
gp 17.18.2 Wohin mit den Servlets: das classes-Verzeichnis
gp 17.18.3 Servlets mit dem Sysdeo-Plugin unter Eclipse
gp 17.18.4 Servlet-Mapping
gp 17.19 Der Lebenszyklus eines Servlets
gp 17.19.1 Initialisierung in init()
gp 17.19.2 Abfragen bei service()
gp 17.19.3 Mehrere Anfragen beim Servlet und die Thread-Sicherheit
gp 17.19.4 Das Ende eines Servlets
gp 17.20 Das HttpServletResponse-Objekt
gp 17.20.1 Wir generieren eine Web-Seite
gp 17.20.2 Binärdaten senden
gp 17.20.3 Komprimierte Daten mit Content-Encoding
gp 17.20.4 Noch mehr über Header, die der Server setzt
gp 17.21 Objekte und Dateien per POST verschicken
gp 17.21.1 Datei-Upload
gp 17.22 Servlets und Sessions
gp 17.23 Weiterleiten und Einbinden von Servlet-Inhalten
gp 17.24 Inter-Servlet-Kommunikation
gp 17.24.1 Daten zwischen Servlets teilen
gp 17.25 Internationalisierung
gp 17.25.1 Die Länderkennung des Anfragers auslesen
gp 17.25.2 Länderkennung für die Ausgabe setzen
gp 17.25.3 Westeuropäische Texte senden
gp 17.26 Tomcat: Spezielles
gp 17.26.1 Tomcat als Service unter Windows NT ausführen
gp 17.26.2 MIME-Types mit Tomcat verbinden
gp 17.26.3 Servlets beim Start laden
gp 17.27 Ein Servlet generiert WAP-Seiten für das Handy
gp 17.27.1 Ein WAP-Handy simulieren
gp 17.27.2 Übersicht der wichtigsten Tags
gp 17.27.3 Der Gateway
gp 17.27.4 WML-Seiten aufbauen
gp 17.27.5 Interessante Links zum Thema Servlets/JSP


Galileo Computing

17.16 Sitzungsverfolgung (Session Tracking)downtop

Im vorigen Kapitel haben wir uns mit dem Problem beschäftigt, wie trotz des zustandslosen Protokolls ein Verlauf auf dem Server verwaltet werden kann. Wir haben dafür die kleinen Informationseinheiten der Cookies eingesetzt, doch es gibt andere Lösungen, die wir noch einmal mit ihren Vor- und Nachteilen zusammenfassen:

gp Cookies
Ein Cookie speichert eine Kennung, so dass der Server den Client erkennt und die Informationen für ihn speziell aufbereitet. Obwohl dies in Java durch die Cookie-Klasse einfach möglich ist, hat dieser Ansatz noch ein paar Schwächen. Es fällt dem Servlet die Aufgabe zu, aus der Cookie-Kennung die entsprechende Sitzung herauszusuchen und die Daten zu holen. Ein weiteres Problem ergibt sich dadurch, dass Cookies zwar möglich sind, aber vom Benutzer abgelehnt werden können, da dieser seine Anonymität aufs Spiel gesetzt sieht. Schaltet der Benutzer in seinem Lieblingsbrowser die Cookies aus, können wir nichts machen. Doch auch wenn Cookies verwendet werden, bleibt die Frage, wie lange der Cookie gültig sein soll. Hier ist zu überlegen, ob die normale Handhabung, dass der Keks nur eine Sitzung übersteht, sinnvoll ist.
gp URL-Rewriting
Da ein Servlet vom Aufrufer Parameter bekommen kann, ist es eine nette Idee, an die URL einen Wert anzuhängen, der die aktuelle Sitzung kennzeichnet. Diese Kennung entspricht dann genau dem Wert des Cookies. Die Lösung ist simpel und funktioniert bei allen Browsern. Der Nachteil auf der Server-Seite ist wiederum, dass uns die Aufgabe zufällt, der Kennung die Sitzung zuzuordnen. Zudem ist Vorsicht geboten, da diese Kennung bei jedem Verweis wieder angehängt wird. Außerdem ist es für den Benutzer sehr unschön, diese Kennungen zu sehen, zumal sie in die Bookmarks übernommen werden. Dies führt zu dem Problem, dass eine Sitzung angesprochen werden kann, die gar nicht mehr existiert. Dies ist ein sehr schwer wiegendes Problem, da die Anhängsel ja nicht wie Cookies automatisch veralten.
gp Versteckte Felder (engl. hidden fields)
In HTML-Seiten lassen sich versteckte Informationen in Formularen anlegen, die dann automatisch beim Versenden mitgeschickt werden. Dies sieht etwa so aus:
      <INPUT TYPE="HIDDEN" NAME="session" VALUE="...">
gp Diese versteckten Informationen können auch genutzt werden, um eine Sitzungs-ID mitzuschicken. Der Vorteil ist, dass wir wieder keine Cookies benötigen und die URL nicht länger wird. Der Nachteil ist, dass die Information immer dynamisch mit eingebaut werden muss.

Cookies erlauben dem Server, den Client wieder zu erkennen und ihn einer Sitzung zuzuordnen. Doch wir haben gesehen, dass unser Servletcode noch einiges an Arbeit investieren muss. Der Cookie musste gesetzt und geholt werden, und wir mussten die Daten in einer Datenstruktur verwalten. Wenn der Benutzer Cookies ablehnt, müssen wir eine zweite Implementierung anbieten, die Sitzungsinformationen an die URL anhängt.

Glücklicherweise entlastet uns die Servlet-API und bietet die Klasse HttpSession an, eine Bibliothek auf hohem Niveau für die Verwaltung einer Sitzung. Sie basiert entweder auf Cookies oder URL-Rewriting, doch das wird von der API transparent gehalten. Als Programmierer bekommen wir so gut wie gar nichts davon mit. Mag der Client keine Kekse, so wandeln wir alle Informationen in URLs um, die wir dann anbieten. Ein Sitzungsobjekt verwaltet auch selbstständig in einer Datenstruktur die gesicherten Daten. Hier fällt für uns keine Arbeit an. Sitzungsobjekte können leicht verwendet werden, wie wir noch sehen werden. Sie können leider keine geschützten Felder nutzen.


Galileo Computing

17.16.1 Das mit einer Sitzung verbundene Objekt HttpSessiondowntop

Jede Sitzung ist mit einem Sitzungsobjekt verbunden, welches die Klasse HttpSession abbildet. Bei JSPs repräsentiert das implizite Objekt session die aktuelle Sitzung.


Galileo Computing

17.16.2 Werte mit einer Sitzung assoziieren und auslesendowntop

Um Informationen mit der Sitzung zu verbinden, verwenden wir die Methode setAttribute(), die einen Schlüssel und einen Wert verbindet. Daten werden mit getAttribute() wieder aus der Datenstruktur gelockt, so wie es folgendes Beispiel zeigt:

List l = (List) session.getAttribute( "artikel" );

Hier verbinden wir mit einem Schlüssel eine Liste von Waren. Im Hintergrund werden die Informationen auf der Server-Seite gesichert. Die Informationen selbst werden nicht in Cookies oder in der URL abgelegt, daher spielt die Größe der Daten auch keine Rolle. Ein HttpSession-Objekt verwaltet einen Assoziativspeicher, der die Wertepaare speichert. Es ist günstig, die Elemente serialisierbar zu gestalten, um die Daten dauerhaft zu speichern.

Werfen wir abschießend einen Blick auf das Programmstück, das eine neue Ware hinzufügt:

List l = (List) session.getAttribute( "artikel" );
if ( l == null )
{
  l = new ArrayList();
  session.setAttribute( "artikel", l );
}
l.add( w );

interface javax.servlet.http.HttpSession

gp Object getAttribute( String name )
Liefert das mit name verbundene Objekt; null, wenn es keine Assoziation gab.
gp Enumeration getAttributeNames()
Liefert eine Aufzählung aller mit der Sitzung verbundenen Objekte.
gp void setAttribute( String name, Object value )
Bindet name mit dem Objekt value an die Sitzung. Existierte das Objekt, so wird es ersetzt. Angemeldete HttpSessionBindingListener werden über die Methode value Bound() beziehungsweise valueUnbound() informiert.
gp void removeAttribute( String name )
Entfernt das Attribut von der Sitzung. Ungültige Namen werden ignoriert. HttpSession BindingListener werden durch Aufruf von valueUnbound() informiert.

Alle Methoden liefern eine IllegalStateException, wenn die Sitzung ungültig ist. Die Methoden putValue() und setValue() sind veraltet und wurden durch setAttribute() und getAttribute() ersetzt.


Galileo Computing

17.16.3 URL-Rewritingdowntop

Das Session-Management sollte im Prinzip unabhängig von der technischen Umsetzung sein. Doch leider greift das Sitzungsmanagement bei URL-Rewriting schon sehr stark ein: Bei jedem Verweis auf eine neue Seite muss die URL entsprechend angepasst werden, denn man muss die Sitzungs-ID mitschicken. Cookies verwalten die Sitzungs-ID völlig anders. Das bedeutet, werden Cookies eingesetzt, dann ändert sich die URL nicht und jeder kann problemlos auf eine neue Seite verweisen. Nur bei URL-Rewriting muss an die URL eine Sitzungskennung angehängt werden.


Beispiel Eine URL für einen Cookie besitzt keine Sitzungskennung. http://localhost/servlet/URLRewritingSession

Mit URL-Rewriting sieht das dann etwa so aus:

http://localhost/servlet/URLRewritingSession;jsessionid=abcde234

Wenn wir innerhalb eines Servlets auf eine andere generierte Seite verweisen wollen, so haben wir eine URL vor uns, zu der wir verzweigen möchten. Die Servlet-API kümmert sich darum, dass zu einer Benutzer-URL die Sitzungs-ID automatisch angehängt wird. Dazu dienen die HttpServletResponse-Methode encodeURL() oder encodeRedirectURL().


Beispiel Es soll aufgrund einer Formularbestätigung auf eine JSP-Seite mit dem Namen validate.jsp verwiesen werden.
<form action='<%= response.encodeURL("/validate.jsp") %>'>

Werden der Verweis und die Kodierung aus Versehen vergessen, dann ist dies das Ende der Sitzung. Ob eine Sitzung mit einem Cookie behandelt wird, lässt sich mit isRequestedSessionIdFromCookie() testen. Dann kann aufgrund einer Fallunterscheidung encodeURL() verwendet werden oder nicht. Allgemein ist es aber nicht schlecht, wenn grundsätzlich alle Verweise innerhalb einer Webapplikation mit encodeURL() gesichert werden. Zwar wird im Fall von Cookies keine Kennung angehängt, aber eine spätere Umstellung fällt dann leichter, wenn der Nutzer einmal Cookies ausschaltet.


Galileo Computing

17.16.4 Zusätzliche Informationentoptop

Ein Sitzungsobjekt verwaltet neben den assoziierten Daten noch weitere Informationen. Jede Sitzung bekommt eine eindeutige ID, die sich mit getId() erfragen lässt. Ist die Sitzung neu, und der Client hat noch nie eine Verbindung gehabt, so gibt isNew() den Wert true zurück. Existiert dann die Sitzung, gibt getCreationTime() ein long zurück - kodiert sind wie üblich die vergangenen Millisekunden seit dem 1.1.1970 -, in dem sich das Erstellungsdatum erfragen lässt. Dagegen erfragt getLastAccessedTime() die Zeit, die seit dem letzen Zugriff durch den Client vergangen ist. Falls der Server die Informationen dauerhaft speichert und der Cookie nicht abläuft, erlaubt dies Meldungen der Art: »Schön, Sie nach zwei Wochen zum fünften Mal bei unserer Partnervermittlung wiederzusehen. Hat's wieder nicht geklappt?«

Das Ende der Sitzung

Eine Sitzung ist nicht automatisch für unendlich lange gültig. Bei Cookies lässt sich der Gültigkeitszeitraum einstellen. Auch Sitzungsobjekte lassen sich in der Zeit anpassen. Die Methode setMaxInactiveInterval() setzt den Wert, den eine Sitzung gültig ist. Ist der Wert negativ, zeigt er an, dass die Sitzung nicht automatisch beendet wird. Die entsprechende Methode getMaxInactiveInterval() liefert die Zeit in Sekunden, in der eine Sitzung gültig ist.


interface javax.servlet.http.HttpSession

gp long getCreationTime()
Gibt in Millisekunden ab dem 1.1.1970 an, wann die Sitzung eröffnet wurde.
gp String getId()
Liefert eine eindeutige Kennung, welche die Sitzung identifiziert.
gp long getLastAccessedTime()
Gibt in Millisekunden ab dem 1.1.1970 zurück, wann der Client zum letzten Mal auf den Server zugegriffen hat.
gp int getMaxInactiveInterval()
void setMaxInactiveInterval( int interval )
gp Liefert und setzt die Zeit, für die der Servlet-Container die Sitzung aufrechterhalten soll, bis sie ungültig wird.
gp boolean isNew()
Der Rückgabewert ist true, wenn die Sitzung neu ist.

Beispiel Zum Schluss wollen wir ein Programm formulieren, welches alle diese Informationen auf einmal ausgibt.

Listing 17.18 sessionTracking.jsp

<%@ page language="java" import="java.util.*" %>
<%
  int cnt = 0;
  if ( session.isNew() )
  {
    out.println( "Willkommen Neuling!\n" );
  }
  else
  {
    out.println( "Hallo alter Freund!\n" );
    String o = (String) session.getAttribute( "cnt" );
    if ( o != null )
      cnt = Integer.parseInt( o );
    cnt++;
  }
  session.setAttribute( "cnt", ""+cnt );
%>
 <p>
 Session-ID: <%= session.getId() %> <p>
 Erzeugt am: <%= new Date(session.getCreationTime()) %> <p>
 Letzter Zugriff: <%= new Date(session.getLastAccessedTime()) %> <p>
 Ungültig in Minuten: <%= session.getMaxInactiveInterval()/60 %> <p>
 Anzahl Zugriffe: <%= cnt %>

Das Programm liefert beispielsweise folgende Ausgabe:

Hallo alter Freund! 
ID: 91410050092487D9B5D0D2A7A3D0F072 
Erzeugt am: Fri Jan 18 20:16:49 CET 2002 
Letzter Zugriff: Fri Jan 18 20:23:33 CET 2002 
Ungültig in Minuten: 30 
Anzahl Zugriffe: 4

Die ID sieht bei jedem Server anders aus. Der Web-Server von Sun erzeugt beispielsweise ganz andere Kennungen.

Mögliche Sicherheitsprobleme mit der ID

Die Sitzungsnummer, die wir auf dem Sitzungsobjekt mit getID() erfragen können, macht nicht bei jedem Servlet-Container eine vertrauensvolle Figur. Denn wenn wir diese erzeugen könnten, so wäre es möglich, unter einer fremden ID Aktionen durchzuführen. Es dürfte selbstverständlich sein, dass das vermieden werden muss. Unter der Servlet-API lässt sich die Berechnung der ID leider nicht anpassen, so dass wir auf eine gute Zufallsgenerierung vertrauen müssen. Diese ist bei Tomcat gegeben, denn die Methode generateSessionId() ruft eine Methode generateId() im SessionIdGenerator auf, die sehr gute Zahlen mit Hilfe der Klasse java.security.SecureRandom konstruiert.


Beispiel Ein paar IDs
h7pueqoxj1. vl7614oxj2, an8kmdoxj3, kzsy0poxj4
qgstxvoxj5, meay0toxj6, sdtltaoxj7, uy3n6uoxj8
D2thahoxj9, ppg4vroxja

Die ID besteht aus sechs Zufallszeichen, drei mit der aktuellen Zeit kodierten Zeichen und einem Zähler, der bei 1 beginnt.

Bei anderen Servern bleibt nur die Lösung, auf HttpSession zu verzichten und mit Cookies eigene IDs zu verwalten.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> 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.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de