Übungsaufgabe 9: Warenkorb Erweitern Sie Ihre E−Shop−Anwendung um einen Warenkorb! In diesen Warenkorb kann der Benutzer Produkte hineinlegen. Der Warenkorb soll auf jeder Seite sichtbar sein. Er zeigt für jedes Produkt im Korb Produktname, Stückzahl, Einzelpreis und Gesamtpreis an. Initial ist der Warenkorb natürlich leer. In der Detailansicht eines Produkts kann der Benutzer das Produkt mit Angabe der Stückzahl zum Warenkorb hinzufügen. In der Warenkorbanzeige ist die Stückzahl eines Produkts modifizierbar. Setzt der Anwender die Stückzahl auf 0, wird das Produkt aus dem Warenkorb entfernt. Die Seite »Kasse« wird später der Abrechnung dienen. In dieser Übungsaufgabe wird der Einkaufszettel mit der Gesamtsumme angezeigt, der Benutzer um Bestätigung gebeten und die Sitzung beendet. Web−Anwendungen mit Java 251 Request−Methoden Zahlreiche Methode zum Erfragen von request−spezifischen Details Beispiel Snoop−Servlet Web−Anwendungen mit Java 252 Snoop−Servlet package de.rainer_klute.servlet; import import import import java.io.*; java.util.*; javax.servlet.*; javax.servlet.http.*; /** * <p>Dieses Servlet analysiert einen HTTP−Request und * zugehörige servletspezifische Klassen.</p> * * @author Rainer Klute */ public class Snoop extends HttpServlet { Web−Anwendungen mit Java 253 Snoop−Servlet /** * <p>Gibt zwei Objekte als eine Zeile einer * HTML−Tabelle aus.</p> */ private void printTableLine(PrintWriter out, Object cell1, Object cell2) { out.println("<tr valign=\"top\">"); printTableCell(out, cell1); printTableCell(out, cell2); out.println("</tr>"); } Web−Anwendungen mit Java 254 Snoop−Servlet /** * <p>Gibt ein Objekt als Tabellenzelle aus.</p> */ private void printTableCell(PrintWriter out, Object cell) { StringBuffer b = new StringBuffer(80); String s = (cell == null ? "<p>null</p>" : cell.toString().trim()); b.append("<td>"); Web−Anwendungen mit Java 255 Snoop−Servlet } if (s.equals("")) b.append("<p>&nbsp;</p>"); else { if (s.charAt(0) == ’<’) b.append(s); else { b.append("<p>"); b.append(s); b.append("</p>"); } } b.append("</td>"); out.println(b.toString()); Web−Anwendungen mit Java 256 Snoop−Servlet /** * <p>Bearbeitet GET−Request.</p> */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Enumeration e; PrintWriter out = response.getWriter(); StringBuffer b = new StringBuffer(50); response.setContentType("text/html"); out.println("<!DOCTYPE html PUBLIC " + "\"−//W3C//DTD HTML 4.0//EN//\">"); out.println("<html>"); out.println("<head>"); out.println("<title>Snoop</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Snoop</h1>"); Web−Anwendungen mit Java 257 Snoop−Servlet /* Informationen zum Servlet */ out.println("<div>"); out.println("<h2>Generic Servlet</h2>"); out.println("<table border=\"1\">"); /* Servlet−Konfiguration */ printTableLine(out, "Servlet Config:", getServletConfig()); /* Servlet−Info */ printTableLine(out, "Servlet Info:", getServletInfo()); Web−Anwendungen mit Java 258 Snoop−Servlet /* Initialisierungsparameter */ b.setLength(0); e = getInitParameterNames(); if (e.hasMoreElements()) { b.append("<table border=\"1\">"); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = getInitParameter(name); b.append("<tr valign=\"top\">"); b.append("<td><p>" + name + "</p></td>"); b.append("<td><p>" + value + "</p></td>"); b.append("</tr>"); } b.append("</table>"); } printTableLine(out, "Init Parameters:", b); Web−Anwendungen mit Java 259 Snoop−Servlet /* Servlet−Kontext */ printTableLine(out, "Servlet Context:", getServletContext()); /* Servlet−Name */ printTableLine(out, "Servlet Name:", getServletName()); out.println("</table>"); out.println("</div>"); /* Informationen zum Request * (nicht HTTP−spezifisch) */ out.println("<div>"); out.println("<h2>Servlet Request</h2>"); out.println("<table border=\"1\">"); Web−Anwendungen mit Java 260 Snoop−Servlet /* Request−Attribute */ b.setLength(0); e = request.getAttributeNames(); if (e.hasMoreElements()) { b.append("<table border=\"1\">"); while (e.hasMoreElements()) { String name = (String) e.nextElement(); Object value = request.getAttribute(name); b.append("<tr valign=\"top\">"); b.append("<td><p>" + name + "</p></td>"); b.append("<td><p>" + value.toString() + "</p></td>"); b.append("</tr>"); } b.append("</table>"); } printTableLine(out, "Attributes:", b); Web−Anwendungen mit Java 261 Snoop−Servlet /* Zeichenkodierung */ printTableLine(out, "Character Encoding:", request.getCharacterEncoding()); /* Anzahl Bytes */ printTableLine(out, "Content Length:", request.getContentLength() + ""); /* Inhaltstyp */ printTableLine(out, "Content Type:", request.getContentType()); /* Bevorzugte Lokalisierung */ printTableLine(out, "Locale:", request.getLocale()); Web−Anwendungen mit Java 262 Snoop−Servlet /* Alle Lokalisierungen */ b.setLength(0); e = request.getLocales(); while (e.hasMoreElements()) { b.append(e.nextElement()); if (e.hasMoreElements()) b.append(", "); } printTableLine(out, "Locales:", b); /* Servlet−Paraeter */ b.setLength(0); e = request.getParameterNames(); if (e.hasMoreElements()) { Web−Anwendungen mit Java 263 Snoop−Servlet b.append("<table border=\"1\">"); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String[] values = request.getParameterValues(name); for (int i = 0; i < values.length; i++) { b.append("<tr valign=\"top\">"); b.append("<td><p>" + (i == 0 ? name : "&nbsp;") + "</p></td>"); b.append("<td><p>" + values[i].toString() + "</p></td>"); b.append("</tr>"); } } b.append("</table>"); } printTableLine(out, "Parameters:", b); Web−Anwendungen mit Java 264 Snoop−Servlet /* Protokoll, z.B. "HTTP/1.0" */ printTableLine(out, "Protocol:", request.getProtocol()); /* IP−Adresse des Clients */ printTableLine(out, "Remote Address:", request.getRemoteAddr()); /* Domain−Name des Clients */ printTableLine(out, "Remote Host", request.getRemoteHost()); /* Schema (i.d.R. "http") */ printTableLine(out, "Scheme:", request.getScheme()); /* Domain−Name des Servers */ printTableLine(out, "Server Name:", request.getServerName()); Web−Anwendungen mit Java 265 Snoop−Servlet /* Nummer des Server−Ports */ printTableLine(out, "Server Port:", request.getServerPort() + ""); /* Sicherer (verschlüsselter) Zugriff? */ printTableLine(out, "Secure:", request.isSecure() + ""); out.println("</table>"); /* Informationen zum Request (HTTP−spezifisch) */ out.println("<h2>HTTP Servlet Request</h2>"); out.println("<table border=\"1\">"); /* Authentifizierungstyp */ printTableLine(out, "Auth Type:", request.getAuthType()); /* Kontextpfad */ printTableLine(out, "Context Path:", request.getContextPath()); Web−Anwendungen mit Java 266 Snoop−Servlet /* Cookies */ b.setLength(0); Cookie[] cookies = request.getCookies(); for (int i = 0; i < cookies.length; i++) b.append(toHTML(cookies[i])); printTableLine(out, "Cookies:", b); /* HTTP−Header */ b.setLength(0); e = request.getHeaderNames(); if (e.hasMoreElements()) { b.append("<table border=\"1\">"); while (e.hasMoreElements()) { String name = (String) e.nextElement(); Enumeration values = request.getHeaders(name); Web−Anwendungen mit Java 267 Snoop−Servlet while (values.hasMoreElements()) { b.append("<tr valign=\"top\">"); b.append("<td><p>" + name + "</p></td>"); b.append("<td><p>" + values.nextElement() + "</p></td>"); b.append("</tr>"); } } b.append("</table>"); } printTableLine(out, "Header:", b); /* HTTP−Methode */ printTableLine(out, "Method:", request.getMethod()); Web−Anwendungen mit Java 268 Snoop−Servlet /* Pfad−Info */ printTableLine(out, "Path Info:", request.getPathInfo()); /* Pfad im Server−Dateisystem */ printTableLine(out, "Path Translated:", request.getPathTranslated()); /* Query−String */ printTableLine(out, "Query String:", request.getQueryString()); /* Name des authentifizierten Benutzers */ printTableLine(out, "Remote User", request.getRemoteUser()); /* ID der vom Client gewünschten Session */ printTableLine(out, "Requested Session ID:", request.getRequestedSessionId()); Web−Anwendungen mit Java 269 Snoop−Servlet /* URI des HTTP−Requests */ printTableLine(out, "Request URI:", request.getRequestURI()); /* Servlet−Pfad */ printTableLine(out, "Servlet Path:", request.getServletPath()); /* Session */ printTableLine(out, "Session:", request.getSession()); /* Authentifizierter Benutzer */ printTableLine(out, "User Principal:", request.getUserPrincipal()); /* Angeforderte Session−ID aus Cookie? */ printTableLine (out, "Is requested session ID from Cookie:", request.isRequestedSessionIdFromCookie() + ""); Web−Anwendungen mit Java 270 Snoop−Servlet /* Angeforderte Session−ID aus URL? */ printTableLine (out, "Is Requested Session ID From URL:", request.isRequestedSessionIdFromURL() + ""); /* Angeforderte Session−ID gültig? */ printTableLine (out, "Is Requested Session ID Valid:", request.isRequestedSessionIdValid() + ""); /* Besitzt authentifizierte Benutzer die * spezifizierte Rolle? */ printTableLine(out, "Is user in role \"Foobar\":", request.isUserInRole("Foobar") + ""); /* Body des HTTP−Requests, soweit noch nicht * ausgewertet */ if (request.getMethod().equals("POST")) { Web−Anwendungen mit Java 271 Snoop−Servlet StringBuffer sb = new StringBuffer(500); int c; /* Versuche, den Reader aus dem Request * zu bekommen. Falls das mißlingt, weiche * auf den InputStream aus. */ try { /* Zeichen aus Reader lesen */ Reader r = request.getReader(); do { c = r.read(); if (c != −1) sb.append((char) c); } while (c != −1); } Web−Anwendungen mit Java 272 Snoop−Servlet } catch (IllegalStateException ex) { /* Zeichen aus InputStream lesen */ InputStream r = request.getInputStream(); do { c = r.read(); if (c != −1) sb.append((char) c); } while (c != −1); } String s = sb.toString(); printTableLine(out, "Data submitted:", s.equals("") ? "&nbsp;" : "<pre>" + sb.toString() + "</pre>"); Web−Anwendungen mit Java 273 Snoop−Servlet out.println("</table>"); } out.println("</div>"); out.println("</body>"); out.println("</html>"); out.println(); /** * <p>Liefert HTML−Darstellung eines Cookies.</p> */ String toHTML(Cookie c) { StringBuffer b = new StringBuffer(); b.append("<table border=\"1\""); Web−Anwendungen mit Java 274 Snoop−Servlet b.append("<tr>"); b.append("<td><p>Name:</p></td>"); b.append("<td><p>"); b.append(c.getName()); b.append("</p></td>"); b.append("</tr>"); b.append("<tr>"); b.append("<td><p>Comment:</p></td>"); b.append("<td><p>"); b.append(c.getComment()); b.append("</p></td>"); b.append("</tr>"); b.append("<tr>"); b.append("<td><p>Domain:</p></td>"); b.append("<td><p>"); b.append(c.getDomain()); b.append("</p></td>"); b.append("</tr>"); Web−Anwendungen mit Java 275 Snoop−Servlet b.append("<tr>"); b.append("<td><p>MaxAge:</p></td>"); b.append("<td><p>"); b.append(c.getMaxAge()); b.append("</p></td>"); b.append("</tr>"); b.append("<tr>"); b.append("<td><p>Path:</p></td>"); b.append("<td><p>"); b.append(c.getPath()); b.append("</p></td>"); b.append("</tr>"); b.append("<tr>"); b.append("<td><p>Secure:</p></td>"); b.append("<td><p>"); b.append(c.getSecure()); b.append("</p></td>"); b.append("</tr>"); Web−Anwendungen mit Java 276 Snoop−Servlet b.append("<tr>"); b.append("<td><p>Value:</p></td>"); b.append("<td><p>"); b.append(c.getValue()); b.append("</p></td>"); b.append("</tr>"); b.append("<tr>"); b.append("<td><p>Version:</p></td>"); b.append("<td><p>"); b.append(c.getVersion()); b.append("</p></td>"); b.append("</tr>"); } b.append("</table>"); return b.toString(); Web−Anwendungen mit Java 277 Snoop−Servlet /** * <p>Bearbeitet POST−Request.</p> */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } Web−Anwendungen mit Java 278 Sicherheit Web−Anwendungen können Ressourcen und Funktionen enthalten, die nicht für jeden zugänglich sein sollen. Beispiel Online−Banking: Kontoinhaber kann Überweisung durchführen. Interessent ohne Konto kann Demokonto nutzen. Sensible Daten müssen während des Transfers vor Ausspähen und Verändern geschützt sein. Beispiel Online−Banking: Kontoauszug ist nur für den Nutzer selbst bestimmt. Dritter darf Überweisung nicht verändern. Web−Anwendungen mit Java 279 Sicherheitsanforderungen Authentifizierung: Mechanismus, durch den ein Kommunikationspartner dem anderen seine Identität nachweist Autorisierung: Mechanismus, der Interaktionen mit Ressourcen auf bestimmte Benutzer beschränkt Vertraulichkeit: Mechanismus, der sicherstellt, daß die übertragene Information nur für die dazu Berechtigten verfügbar ist und während der Übertragung nicht kompromittiert wird Integrität: Mechanismus, der sicherstellt, daß Dritte keine Daten während des Transits verändern Web−Anwendungen mit Java 280 Authentifizierungsmechanismen Wer ist der Zugreifer? HTTP Basic Authentication Ältestes und am weitesten verbreitete Verfahren User−ID und Kennwort werden de facto im Klartext übertragen. Base−64−Kodierung Keine Authentifizierung des Servers Unverschlüsselte Datenübertragung Unsicher Kryptographisch sicher mit HTTPS Web−Anwendungen mit Java 281 Authentifizierungsmechanismen HTTP Digest Authentication Keine Übertragung des Kennworts Client weist nach, daß er im Besitz des korrekten Kennworts ist. Challenge−/Response−Verfahren Unverschlüsselte Übertragung der Nutzdaten Keine weite Verbreitung Web−Anwendungen mit Java 282 Authentifizierungsmechanismen HTTPS Client Authentication HTTPS: HTTP über SSL (Secure Socket Layer) Kryptographisch starkes Authentifizierungsverfahren Client benötigt Zertifikat (Public Key Certificate, PKC) Verschlüsselte Übertragung des gesamten Verkehrs (Benutzername, Kennwort, Nutzdaten) Web−Anwendungen mit Java 283 Authentifizierungsmechanismen Formularbasierte Authentifizierung Eingabe von User−ID und Kennwort im HTML−Formular Paßt zum visuellen Design der Anwendung Felder j_username und j_password FORM−Attribut ACTION lautet j_security_check. <form action="j_security_check" method="post"> <p>Name: <input name="j_username"></p> <p>Kennwort: <input type="password" name="j_password"></p> <p><input type="submit" value="Login"></p> </form> Klartextübertragung von User−ID und Kennwort Kryptographisch sicher mit HTTPS Web−Anwendungen mit Java 284 Deklarative Sicherheit Sicherheitseinstellungen außerhalb der Anwendung Einstellungen im Deployment descriptor WEB−INF/web.xml Gelten jeweils pro Anwendung Für welche Ressourcen und für welche HTTP− Methoden Beschränkungen einrichten? Wer darf auf was wie zugreifen? Welche Verfahren zur Authentifizierung und zur Übertragung verwenden? Web−Anwendungen mit Java 285 Programmierte Sicherheit Sicherheitseinstellungen innerhalb der Anwendung Unterschiedliches Verhalten der Anwendung je nach Rolle des Benutzers Beispiel (Rollen im E−Shop): Anonymer Kunde darf Produktinformationen und Preise lesen und natürlich Produkte bestellen. Authentifizierter Kunde sieht Preise gemäß seiner individuellen Rabattklasse. Produktmanager aktualisiert Preise und Produktinformationen. Autorisierung sollte nur über Rollen erfolgen, nicht über Benutzernamen. Web−Anwendungen mit Java 286 Programmierte Sicherheit Methoden in HttpServletRequest: String getRemoteUser() Name des authentifizierten Benutzers oder null java.security.Principal getUserPrincipal() Principal−Objekt des authentifizierten Benutzer oder null Entspricht einer eindeutigen Identität, z.B. Person, Organisation, Login−Name, Benutzergruppe Wird von diversen Klassen in java.security.acl und java.security.cert verwendet. boolean isUserInRole(String role) Stellt fest, ob authentifizierter Benutzer die angegebene Rolle besitzt. Web−Anwendungen mit Java 287 Benutzer und Rollen Web−Anwendung benötigt Zuordnung Benutzer Kennwort Rollen Einrichten der Zuordnungen nicht standardisiert spezifisch für den jeweiligen Servlet−Container Web−Anwendungen mit Java 288 Benutzer und Rollen bei Tomcat 4 Benutzer, Kennwort und Rollen stammen aus... Datenbank: JDBCRealm JNDI−Verzeichnisdienst, z.B. LDAP: JNDIRealm Textdatei: MemoryRealm, liest aus conf/tomcat− users.xml Geeignet für geringe Benutzeranzahl Keine Änderungen im laufenden Betrieb möglich, z.B. Registrieren neuer Benutzer Einstellungen gelten wahlweise für alle Anwendungen, für alle Anwendungen auf einem virtuellen Host, für eine Anwendung. Web−Anwendungen mit Java 289 Sicherheit konfigurieren <security−constraint> <web−resource−collection> <web−resource−name>Test</web−resource−name> <url−pattern>/Snoop/*</url−pattern> <http−method>GET</http−method> <http−method>POST</http−method> </web−resource−collection> <auth−constraint> <role−name>Snooper</role−name> <role−name>Webmaster</role−name> </auth−constraint> <user−data−constraint> <transport−guarantee>CONFIDENTIAL</transport−guarantee> </user−data−constraint> </security−constraint> Web−Anwendungen mit Java 290 Sicherheit konfigurieren Einzelheiten siehe DTD web−app_2_3.dtd! Element security−constraint Trifft Sicherheitseinstellungen für eine oder mehr Ressource−Collections. Enthält web−resource−collection, auth−constraint, user− data−constraint. Element web−resource−collection Spezifiziert eine Zusammenstellung von Ressourcen und zugehörigen HTTP−Methoden. Enthält url−pattern und http−method. Web−Anwendungen mit Java 291 Sicherheit konfigurieren Element url−pattern spezifiziert eine oder mehrere Ressourcen. Darf mehrfach vorkommen. /foo/bar/*: alle mit »/foo/bar« beginnenden URIs *.foobar: alle mit ».foobar« endenden URIs Alle anderen Strings sind vollständige URIs. Element http−method spezifiziert eine HTTP−Methode. Darf mehrfach vorkommen. Falls http−method fehlt: alle HTTP−Methoden Web−Anwendungen mit Java 292 Sicherheit konfigurieren Element auth−constraint Enthält role−name. Element role−name Spezifiziert den Namen einer Rolle, die den Zugriff gewährt. Darf mehrfach vorkommen. Web−Anwendungen mit Java 293 Sicherheit konfigurieren Element user−data−constraint Spezifiziert Einschränkungen für die Nutzdaten. Enthält transport−guarantee. Element transport−guarantee Anforderungen für den Datentransport NONE: keine Anforderungen. Daten dürfen im Klartext übertragen werden. INTEGRAL: Daten dürfen während des Transports gelesen, aber nicht geändert werden. (Erfordert SSL.) CONFIDENTIAL: Daten können während des Transports nicht gelesen werden. (Erfordert SSL.) Web−Anwendungen mit Java 294 Login Zugriff auf die erste geschützte Ressource erfordert Authentifizierung des Benutzers. Deployment descriptor definiert Authentifizierungsverfahren. Beispiel (HTTP Basic Authentication): <login−config> <auth−method>BASIC</auth−method> <realm−name>RootApplication</realm−name> </login−config> Beispiel (Digest Authentication): <login−config> <auth−method>DIGEST</auth−method> <realm−name>RootApplication</realm−name> </login−config> Web−Anwendungen mit Java 295 Login Web−Anwendungen mit Java 296 Login Beispiel (Formularbasierte Authentifizierung): <login−config> <auth−method>FORM</auth−method> <form−login−config> <form−login−page>/Login.html</form−login−page> <form−error−page>/LoginError.html</form−error−page> </form−login−config> </login−config> Web−Anwendungen mit Java 297 Login Web−Anwendungen mit Java 298 HTTPS HTTPS: HTTP over SSL SSL: Secure Socket Layer Client authentifiziert den Server. Server benötigt Schlüsselpaar Privater Schlüssel Öffentlicher, zertifizierter Schlüssel (Zertifikat) Verschlüsselte Datenübertragung Ausgefüllte Formularfelder Persönliche Daten, Kennwörter, Kreditkartennummern Daten vom Server Web−Anwendungen mit Java 299 HTTPS mit Tomcat Tomcat benötigt JSSE JSSE: Java Secure Socket Extension SSL benötigt RSA−Verschlüsselung Seit 2000 patentfrei Starke Kryptographie fällt nicht mehr unter das amerikanische Kriegswaffenkontrollgesetz. Sun Microsystems kann JSSE von exportiert werden. Alternativen verfügbar In J2SE SDK 1.4 bereits enthalten, sonst von http://java.sun.com/products/jsse herunterladen Web−Anwendungen mit Java 300