Ok Super!
Besonders das mit den Sessions liegt mir am Herzen.. da ich da nicht ganz durchblicke..
Ob ich das eh richtig mache...
index.jsp:
Java:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><jsp:useBean id="db"class="beans.Datenbank" scope="session"/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><body><%try{String wrong = request.getParameter("wrong");if(wrong.equals("true")){
out.println("Username oder Passwort vergessen");}}catch(Exception e){System.out.println(e.getMessage());}if(!db.checkSession(session.getId())){%><form action="login.jsp" method="post">Name:<input type="text" name="username" maxlength="20"/><br>Passwort:<input type="password" name="password" maxlength="20"/><br><input type="submit" value="Submit"/></form><%}else{
response.sendRedirect("login.jsp");}%></body></html>
login.jsp:
Java:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%><jsp:useBean id="db"class="beans.Datenbank" scope="session"/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><%String username = request.getParameter("username");String password = request.getParameter("password");if(username ==""|| password ==""){
response.sendRedirect("index.jsp?wrong=true");}else{System.out.println(session.getId());
db.setSession(username,session.getId());if(db.checkSession(session.getId())|| db.checkUser(username,password)){%><a href="info.jsp">Info</a><br/><a href="logout.jsp">Ausloggen</a><%}else{
response.sendRedirect("index.jsp");}}%></body></html>
info.jsp:
Java:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><jsp:useBean id="db"class="beans.Datenbank" scope="session"/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><body><%if(db.checkSession(session.getId())){
out.println("jo bin eingeloggt super...");%><a href="login.jsp">Login</a><%}else{
response.sendRedirect("index.jsp");}%></body></html>
logout.jsp:
Java:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><jsp:useBean id="db"class="beans.Datenbank" scope="session"/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><body><%if(db.checkSession(session.getId())){System.out.println("session zerstört");
session.invalidate();
db.unsetSession(session.getId());
response.sendRedirect("index.jsp");}else{
response.sendRedirect("index.jsp");}%></body></html>
Datenbank.java
Java:
package beans;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.SQLException;importcom.mysql.jdbc.PreparedStatement;importcom.mysql.jdbc.Statement;publicclassDatenbank{privateConnection con;privateConnectionstartDb(){try{Class.forName("com.mysql.jdbc.Driver").newInstance();
con =DriverManager.getConnection("jdbc:mysql://localhost:3306/testlogin","root","test");System.out.println("jdbc driver instantiated");}catch(Exception e){System.err.println("cant instantiate jdbc Driver");}return con;}publicvoidcloseDb(){try{
con.close();System.out.println("Datenbank wurde geschlossen");}catch(Exception e){System.err.println("Datenbank konnte nicht geschlossen werden");}}publicvoidsetSession(String username,String sessionid){Connection con =startDb();try{PreparedStatement pstmt =(PreparedStatement)con.prepareStatement("UPDATE user SET sessionid=? WHERE name=?");
pstmt.setString(1,sessionid);
pstmt.setString(2,username);
pstmt.executeUpdate();}catch(SQLException e){
e.printStackTrace();System.err.println("Konnte Sessionid nicht setzen");}}publicvoidunsetSession(String sessionid){Connection con =startDb();try{PreparedStatement pstmt =(PreparedStatement)con.prepareStatement("UPDATE user SET sessionid=? WHERE sessionid=?");
pstmt.setString(1,"");
pstmt.setString(2,sessionid);
pstmt.executeUpdate();}catch(SQLException e){
e.printStackTrace();System.err.println("Konnte Sessionid nicht löschen");}}publicvoidcreateUser(String user,String password,String id){Connection con =startDb();try{PreparedStatement pstmt =(PreparedStatement)con.prepareStatement("INSERT INTO user(name,password,sessionid) VALUES(?,?,?)");
pstmt.setString(1,user);
pstmt.setString(2,password);
pstmt.setString(3, id);
pstmt.executeUpdate();}catch(SQLException e){
e.printStackTrace();System.err.println("Konnte User nicht erstellen");}}publicbooleancheckSession(String sessionid){Connection con =startDb();PreparedStatement pstmt =null;ResultSet rs;boolean ok =false;try{
pstmt =(PreparedStatement)con.prepareStatement("SELECT count(*) FROM user WHERE sessionid= ?");
pstmt.setString(1, sessionid);
rs = pstmt.executeQuery();
rs.next();if(rs.getInt(1)>0&& rs.getInt(1)<2){
ok =true;}}catch(SQLException e){System.err.println("select session fehler");
e.printStackTrace();}return ok;}publicbooleancheckUser(String username,String password){Connection con =startDb();PreparedStatement pstmt =null;ResultSet rs;boolean ok =false;try{
pstmt =(PreparedStatement)con.prepareStatement("SELECT count(*) FROM user WHERE name = ? AND password = ?");
pstmt.setString(1,username);
pstmt.setString(2,password);
rs = pstmt.executeQuery();
rs.next();if(rs.getInt(1)>0&& rs.getInt(1)<2){
ok =true;}}catch(SQLException e){System.err.println("konnte user nicht finden");
e.printStackTrace();}return ok;}}
Heute benutzt man keine Scriptlets (Java-Code in der HTML-Seite) mehr. Das wird über Servlets geregelt. Dann umgehst du auch gleich viele Probleme und musst deinen Code später nicht mehr komplett umbauen.
Das mit den PreparedStatements sieht schon mal gut aus; reicht aber nicht aus um dich und deine Webseitenbesucher 100% zu schützen. Du musst die Eingaben gesondert nochmal überprüfen, neben SQL-Injections gibt es z.B. auch noch HTML-Injections. Einfach mal nach Code-Injections googeln (wenn das noch nicht getan hast). Das ist ein riesiges aber sehr wichtiges Thema.
Auch brauchst du nicht jedes Mal eine Connection zur DB erstellen. Die Besonderheit, die Java bspw. von PHP abhept, ist, dass du die Connection nach einem Statement noch weiterhin benutzen kannst - deine Webapplikation wird, im Gegensatz zu PHP, ja nicht geschlossen. Dazu benötigst du aber Servlets und Singletons, ansonsten kannst du die Wiederverwendung (und den daraus resultierenden Performancegewinn) von Objekten vergessen.
Guck dir lieber sofort Servlets an. Ist zwar mühsam, aber sobald die Applikation größer wird musst du so oder so umsteigen. Bei PHP wäre sowas noch im Bereich des machbaren (wobei man selbst da Code und Oberfläche trennt) - bei einer JSP bekommst du technisch bedingt aber zu schnell Problemen. Also lieber gleich umsteigen, bevor dich in ein paar Monaten drüber ärgerst.
Also generell hat Antoras recht aber trotzdem sind Scriptlets nicht generell zu verabscheuen. Für kleine Dinge ist es sicher nichts schlechtes, wenn auch Heute eher unüblich geworden, was aber eben auch nicht zuletzt an der Komplexität der meisten Anwendungen liegt.
Generell sind aber Servlets den Scriptlets vorzuziehen. Zum Einarbeiten sind Scriptlets aber nicht schlecht.
Nö, die Vorgehensweise dafür lautet nicht Servlets oder JSP, sondern Servlets und JSP. Deswegen hab ich die JSTL erwähnt. Dann kommst du auch zu den Templates und all deine alten Probleme scheinen erst mal gelöst...
Nur als Anmerkung. Du kannst zu Testzwecken einen Tomcat auch lokal auf deinem eigenen Rechner installieren. Dafür brauchst du keinen extra Server.
Edit: Ups, hab gar nicht gesehen, das du das ja schon hast. Vergest also den Unsinn, den ich mal wieder erzähle, sorry.
Ok hab jetzt den Code umgebaut.
Singletons berücksichtigt, sowie versucht Logik und Präsentation zu trennen.
Auf HTML Injections hab ich noch keine Rücksicht genommen, da das Thema vorraussichtlich doch ein wenig komplexer ist.
Ebenso, hab ich jetzt darauf verzichtet meine sessionID in die Datenbank zum jeweiligen User zu schreiben.
Maybe hab ich das jetzt falsch verstanden, aber es reicht ja praktisch wenn ich bei erfolgreicher Anmeldung einmal mache: session.setAttribute("login",true); und das dann auf jeder Seite abfrage ob das eh nicht session.getAttribute("login") != null ist oder?
Anbei jetzt der verbesserte Code... bitte nochmal drüberzusehen.. hat mich jetzt einiges an Arbeit gekostet...
index.jsp:
Java:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><body><c:if test="${param.wrong == 'true'}"><c:out value="Username oder Password vergessen"/></c:if><c:choose><c:when test="${sessionScope[\"login\"] == null}"><form action="LoginServ" method="post">Name:<input type="text" name="username" maxlength="20"/><br>Passwort:<input type="password" name="password" maxlength="20"/><br><input type="submit" value="Submit"/></form></c:when><c:otherwise><c:out value="Bereits angemeldet"/></c:otherwise></c:choose></body></html>
package beans;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.SQLException;importcom.mysql.jdbc.PreparedStatement;publicclassDatenbank{privateConnection con;privatestaticDatenbank instance =null;privateDatenbank(){}publicstaticDatenbankgetInstance(){if(instance ==null){
instance =newDatenbank();}return instance;}/**
* Startup Database
* @return Connection
*/privateConnectionstartDb(){try{Class.forName("com.mysql.jdbc.Driver").newInstance();
con =DriverManager.getConnection("jdbc:mysql://localhost:3306/testlogin","root","test");System.out.println("jdbc driver instantiated");}catch(Exception e){System.err.println("cant instantiate jdbc Driver");}return con;}/**
* close Database
*/publicvoidcloseDb(){try{
con.close();System.out.println("Datenbank wurde geschlossen");}catch(Exception e){System.err.println("Datenbank konnte nicht geschlossen werden");}}/**
* set Session in in user Database, NOT IN USE RIGHT NOW
* @param username
* @param sessionid
*/publicvoidsetSession(String username,String sessionid){if(con ==null){
con =startDb();}try{PreparedStatement pstmt =(PreparedStatement)con.prepareStatement("UPDATE user SET sessionid=? WHERE name=?");
pstmt.setString(1,sessionid);
pstmt.setString(2,username);
pstmt.executeUpdate();}catch(SQLException e){
e.printStackTrace();System.err.println("Konnte Sessionid nicht setzen");}}/**
* unSet Session in in user Database, NOT IN USE RIGHT NOW
* @param sessionid
*/publicvoidunsetSession(String sessionid){if(con ==null){
con =startDb();}try{PreparedStatement pstmt =(PreparedStatement)con.prepareStatement("UPDATE user SET sessionid=? WHERE sessionid=?");
pstmt.setString(1,"");
pstmt.setString(2,sessionid);
pstmt.executeUpdate();}catch(SQLException e){
e.printStackTrace();System.err.println("Konnte Sessionid nicht löschen");}}/**
* create user in user Database
* @param user
* @param password
* @param id
*/publicvoidcreateUser(String user,String password,String id){if(con ==null){
con =startDb();}try{PreparedStatement pstmt =(PreparedStatement)con.prepareStatement("INSERT INTO user(name,password,sessionid) VALUES(?,?,?)");
pstmt.setString(1,user);
pstmt.setString(2,password);
pstmt.setString(3, id);
pstmt.executeUpdate();}catch(SQLException e){
e.printStackTrace();System.err.println("Konnte User nicht erstellen");}}/**
* checks if there is already a session with this sessionId, NOT IN USE RIGHT NOW
* @param sessionid
* @return
*/publicbooleancheckSession(String sessionid){if(con ==null){
con =startDb();}PreparedStatement pstmt =null;ResultSet rs;boolean ok =false;try{
pstmt =(PreparedStatement)con.prepareStatement("SELECT count(*) FROM user WHERE sessionid= ?");
pstmt.setString(1, sessionid);
rs = pstmt.executeQuery();
rs.next();if(rs.getInt(1)>0&& rs.getInt(1)<2){
ok =true;}}catch(SQLException e){System.err.println("select session fehler");
e.printStackTrace();}return ok;}/**
* checks if user is registered
* @param username
* @param password
* @return
*/publicbooleancheckUser(String username,String password){if(con ==null){
con =startDb();}PreparedStatement pstmt =null;ResultSet rs;boolean ok =false;try{
pstmt =(PreparedStatement)con.prepareStatement("SELECT count(*) FROM user WHERE name = ? AND password = ?");
pstmt.setString(1,username);
pstmt.setString(2,password);
rs = pstmt.executeQuery();
rs.next();if(rs.getInt(1)>0&& rs.getInt(1)<2){
ok =true;}}catch(SQLException e){System.err.println("konnte user nicht finden");
e.printStackTrace();}return ok;}}
mach lieber so etwas: [c]if ("".equals(username))[/c]
Das macht man eigentlich auch nicht:
Java:
response.sendRedirect("index.jsp?wrong=true");
Dafür gibt es die Methoden setAttribute(...) des Requests. Wenn du die Methode nutzt, erscheint in der HTML-Seite später das wrong-Attribut nicht mehr. Es sei denn du willst es unbedingt in der URL haben...
Was soll die erste Abfrage in Zeile 49 deines Servlets bringen? Du weist doch, dass das Attribut login ungleich null ist - davor fügst du es ja hinzu. Auch die zweite Afrage macht keinen Sinn, da ja ein Account existieren muss um sich überhaupt einzuloggen.
Und der darauf folgende HTML-Code geht mal gar nicht. Setze da wieder ein Attribut (z.B. showInfo) und frag über die JSTL ab ob es gesetzt wurde.
In deinem LogoutServ brauchst du das else-Argument der doGet-Methode nicht. Ein sendRedirect(...) nach dem if genügt.
Dass die DB ein Singleton ist, ist gut. Allerdings solltest du die Connection als static deklarieren und sie mit der DB erstellen. Die Methoden zum Öffnen und Schließen brauchst du nicht. Es existiert nur eine Connection (natürlich nur solange nicht mehrere Request gleichzeitig auf die DB zugreifen - dann bräuchtest du einen Connection Pool). Connections sollten idealerweise nicht geschlossen werden.
Die Sessions brauchst du auch nicht zu serialisieren. Es genügt sie im RAM zu lassen, da die Webapplikation nicht geschlossen wird (=> wieder ein statisches Feld).
XSS ist relativ einfach zu umgehen. Einfach alle Zeichen vor der der Weiterverarbeitung überprüfen. Wenn besondere Zeichen dabei sind (<,>,' usw.), dann maskierst du die entweder oder ersetzt sie durch eine Zeichenfolge (z.B. > ).
Was soll die erste Abfrage in Zeile 49 deines Servlets bringen? Du weist doch, dass das Attribut login ungleich null ist - davor fügst du es ja hinzu. Auch die zweite Afrage macht keinen Sinn, da ja ein Account existieren muss um sich überhaupt einzuloggen.
Und der darauf folgende HTML-Code geht mal gar nicht. Setze da wieder ein Attribut (z.B. showInfo) und frag über die JSTL ab ob es gesetzt wurde.
Ja das ist mir selber auch bisl komisch vorgekommen.. nur.. ich hab mich gefragt wann soll er die Session sonst setzen?
Ich könnts so machen..
Ich prüfe ab ob Username und Passwort in der Datenbank vorkommen.. wenn ja, setz ich die Session und er kann alles weitere sehen.
Wenn er nochmal auf die Seite draufkommt, prüft er ja nicht nach Username und Passwort sondern nach der Session.. eigentlich bräucht ich da noch einen weiteren Zweig...
Was soll das bringen wenn ich hier ein Attribut für den html code setze und dann über jslt abfrage? Möcht ja einfach nur 2 Links haben? Oder meinst du ich soll die gleich nur über jslt machen?
Das mit den Sessions ist eigentlich die falsche Vorgehensweise die du da hast. Die Session wird automatisch erstellt und von deinem Application Server automatisch verwaltet. Du müsstest eine neue Session erstellen, dir diese "merken", ein Cookie im Browser des Anwenders erstellen und dann später überprüfen ob die gespeicherte Session im Cookie mit der des Servers übereinstimmt. Dann brauchst du auch keine extra Variable mehr setzen.
Java:
username.equals("")
genau falsch rum. Guck dir nochmal an was ich geschrieben hab. Grund: Wenn username null ist, dann bekommst du eine NPE (da du versuchst eine Methode von keinem existierendem Objekt aufzurufen). Wenn du aber username an equals übergibst, dann passiert nichts, da equals(...) so programmiert ist, dass eben nichts passiert und false zurückgegeben wird.
Was soll das bringen wenn ich hier ein Attribut für den html code setze und dann über jslt abfrage? Möcht ja einfach nur 2 Links haben? Oder meinst du ich soll die gleich nur über jslt machen?
Der Gedanke ist, dass kein Code aus dem View im Controller landet. Also lieber eine Variable setzen und diese mit JSLT auf true überprüfen, als HTML-Code in das Servlet zu schreiben. Ist bei dir jetzt nicht weiter schlimm, da du ja auf keine JSP weiterleiten tust, aber sobald das mal mehr wie zwei Zeilen werden bist schnell glücklich wenn eine JSP hast.
Serialisierung bedeutet, dass Daten auf einen nichtflüchtigen Datenträger geschrieben und von dort wieder geladen werden (genaueres verrät Google). Ist mit Sessions in Java aber nicht unbedingt geschickt, da das Speichern und Laden auf die HD länger dauert als beim RAM. Deswegen ein Singleton erstellen, in dem alle Session verwaltet werden.
Versuche jetzt gerade einen Warenkorb dazu zu basteln..
Bräucht ein wenig Input bitte..
Ich speicher mir wenn ich mich einlogge meine SessionId in eine Datenbank
Für Servlets hab ich mir eine Methode in einer Beans gebaut, die die aktuelle Session mit der in der Datenbank vergleicht um zu sehen ob man eingeloggt ist.
Wie jedoch kann ich das bei JSP's abfragen, ohne dabei auf Skriptlets zurückgreifen zu müssen?
Soll ich mir hier zusätzlich zur Datenbankspeicherung auch noch ein Attribut setzen, was ich dann in den JSP's abfrage!?
Der Warenkorb wird bei mir jedesmal wenn ich mich auslogge geleert. Ist das sinnvoll? (Ich möchte keine extra Datenbanktabelle verwenden, wäre auch nicht so schwer umzusetzen, im Moment mach ichs rein über sessions)