Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Per la comprensione del presente articolo, occorre aver assimilato i concetti esposti nell'articolo “Prototipo autenticazione utente” (file autenticazione_2.pdf). (Tratto da Wikipedia, l'enciclopedia libera) Funzione MD5 L'MD5 (acronimo di Message Digest algorithm 5) è un algoritmo per la crittografia dei dati a senso unico realizzato da Ronald Rivest (nella foto) nel 1991 e standardizzato con la RFC 1321. Questo tipo di codifica prende in input una stringa di lunghezza arbitraria e ne produce in output un'altra a 128 bit (ovvero con lunghezza fissa di 32 valori esadecimali, indipendentemente dalla stringa di input) che può essere usata per calcolare la firma digitale dell'input. La codifica avviene molto velocemente e si presuppone che l'output (noto anche come "MD5 Checksum" o "MD5 Hash") restituito sia univoco (ovvero si ritiene che sia impossibile, o meglio, che sia altamente improbabile ottenere con due diverse stringhe in input una stessa firma digitale in output) e che non ci sia possibilità, se non per tentativi, di risalire alla stringa di input partendo dalla stringa di output (la gamma di possibili valori in output è pari a 16 alla 32esima potenza). Applicazione pratica dell'MD5 La crittografia tramite algoritmo MD5 viene applicata in tutti i settori dell'informatica che lavorano con il supporto delle firme digitali o che comunque trattano dati sensibili. Ad esempio, viene utilizzata per controllare che uno scambio di dati sia avvenuto senza perdite, semplicemente attraverso il confronto della stringa prodotta dal file inviato con quella prodotta dal file ricevuto. Con lo stesso metodo si può verificare se il contenuto di un file è cambiato (funzione utilizzata dai motori di ricerca per capire se una pagina deve essere nuovamente indicizzata). È diffuso anche come supporto per l'autenticazione degli utenti attraverso i linguaggi di scripting Web server-side (PHP in particolare): durante la registrazione di un utente su un portale internet, la password scelta durante il processo verrà codificata tramite MD5 e la sua firma digitale verrà memorizzata nel database (o in qualsivoglia contenitore di dati). autenticazione_3.pdf Pag. 1/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Successivamente, durante il login la password immessa dall'utente subirà lo stesso trattamento e verrà confrontata con la copia in possesso del server, per avere la certezza dell'autenticità del login. (Fine testo tratto da Wikipedia) Esempio con Php E' facilissimo costruire un piccolo esempio di utilizzo della funzione md5 in Php (md5 è una funzione built-in del linguaggio). File md5.php <?php $a="pippo"; echo ($a."=".md5($a)); ?> Collochiamo la pagina nella cartella htdocs di Xampp e digitiamo la url nel browser http://localhost/md5.php. Otteniamo una sequenza di 32 valori esadecimali come previsto dalla definizione vista precedentemente. Esempio con Jsp In java, le cose sono leggermente più complesse. Scriviamo, come avviene normalmente con una qualsiasi applicazione java, prima di tutto una classe (che chiameremo MD5) e dotiamo questa classe di un metodo statico che chiameremo md5 (in minuscolo). Il metodo accetta come parametro di input una stringa e restituisce in output una nuova stringa che rappresenta l'impronta della stringa iniziale, in accordo autenticazione_3.pdf Pag. 2/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] con la definizione. /* * MD5.java */ package it.itiscastelli.classi; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * * @author maurizio */ public class MD5 { public MD5() { } public static String md5(String message) throws NoSuchAlgorithmException { // utilizziamo la classe MessageDigest del package java.security MessageDigest md; try { // getInstance vuole come argomento la costante “MD5” che rappresenta // l'algoritmo md5 md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException ex) { throw new NoSuchAlgorithmException("Errore. Non esiste un tale algoritmo."); } // stringbuffer di appoggio StringBuffer sb = new StringBuffer(); // il metodo digest vuole come argomento un array di byte e restituisce un array // di byte byte[] messDig5 = md.digest(message.getBytes()); // trasformiamo l'array di byte in una stringa for( int i = 0 ; i < messDig5.length ; i++ ) { String tmpStr = "0"+Integer.toHexString( (0xff & messDig5[i])); sb.append(tmpStr.substring(tmpStr.length()-2)); } // restituiamo la stringa ottenuta da sb return sb.toString(); } // fine metodo md5 } // fine classe MD5 autenticazione_3.pdf Pag. 3/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Poi progettiamo una piccola web application (MD5WebApp) con la quale testare il metodo appena descritto. File index.jsp <%@page import="it.itiscastelli.classi.*" %> <%@page contentType="text/html" pageEncoding="ISO-8859-2"%> <!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-2"> <title>Prova MD5</title> <style> body, td, tr { font-family: Verdana, Arial, "Trebuchet MS"; font-size: 13px; } </style> </head> <body> <h3>Hello MD5!</h3> <% String messaggio = "pippo"; out.println( messaggio + "=" + MD5.md5(messaggio) ); %> </body> </html> autenticazione_3.pdf Pag. 4/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Come si può facilmente vedere, la stringa ottenuta è esattamente la stessa rispetto a quella ottenuta mediante Php. Database MySQL users_db Prepariamo ora un database MySQL users_db e la tabella users_tbl con la seguente struttura: Il campo id rappresenta la chiave primaria della nostra tabella (di tipo autoincrement). Il campo password riceverà le password criptate degli utenti mediante il metodo md5 come spiegato precedentemente. Per gestire più facilmente l'inserimento dei dati, utilizziamo il noto applicativo PhpMyAdmin che è dotato di una comoda interfaccia web. Prepariamo alcuni dati di prova sfruttando uno dei due applicativi (quello in Php o quello in Jsp) descritti precedentemente. La web application PasswordDBWebApp Il nostro sistema di autenticazione utente è costituito da 3 classi (Utente, DAO e MD5) e 3 ulteriori pagine web (in aggiunta a index.jsp), login.jsp, riservata.jsp, logout.jsp. NetBeans 5.5 Ricordatevi di scaricare i driver di MySQL se usate una versione meno recente della 6.1 di NetBeans. Se invece usate NetBeans 6.1 o NetBeans 6.5, allora i driver sono già inclusi in NetBeans e potete facilmente aggiungerli al progetto (tasto destro del mouse sul progetto > Proprietà > Librerie > MySQL Driver). Per coloro che disponessero invece di NetBeans 5.5, ecco la corretta procedura per aggiungere i driver java (noti come MySQL Connector/JDBC): dal link http://www.mysql.com/products/connector/j/ (oppure dal sito della Sun visto che l'azienda che produce MySQL è stata appena acquisita da Sun MicroSystem) scarichiamo il file .tar.gz o il file .zip, poi estraiamo solo il file mysql-connector-java5.1.5-bin.jar e copiamolo ora nella cartella lib (apriamo la cartella web, poi andiamo in WEB-INF e creiamo la cartella lib all'interno). E' tutto. autenticazione_3.pdf Pag. 5/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Ecco ora il codice del nostro applicativo: /* * DAO.java */ package it.itiscastelli.classi; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.GregorianCalendar; /** * * @author maurizio */ public class DAO implements java.io.Serializable { private Connection conn; public DAO() throws ClassNotFoundException, SQLException { // } // chiusura della connessione public void closeConn() throws SQLException { getConn().close(); } // fine metodo closeConn() // caricamento dei driver e apertura della connessione al database public void openConn() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver"); System.out.println("Driver caricati."); setConn(DriverManager.getConnection("jdbc:mysql://localhost:3306/users_db? user=root&password=")); System.out.println("Connessione stabilita."); } // fine metodo openConn() public Utente cercaUtente(String username, String password) throws Exception, SQLException { Statement st = null; ResultSet rs = null; if (username.equals("") && password.equals("")) throw new Exception("Attenzione! Non puoi lasciare username e password vuoti."); autenticazione_3.pdf Pag. 6/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] if (username.equals("")) throw new Exception("Attenzione! Non puoi lasciare il campo username vuoto."); if (password.equals("")) throw new Exception("Attenzione! Non puoi lasciare il campo password vuoto."); try { String sql="SELECT * FROM users_tbl WHERE username='"+username+"' AND password='"+it.itiscastelli.classi.MD5.md5(password)+"'"; st=conn.createStatement(); rs = st.executeQuery(sql); if (!rs.next()) { // record non trovato rs.close(); st.close(); //conn.close(); return null; } else { // record individuato it.itiscastelli.classi.Utente v = new it.itiscastelli.classi.Utente(); v.setUsername(rs.getString("username")); v.setLuogoDiNascita(rs.getString("luogo_di_nascita")); Date dataNascita = rs.getDate("data_di_nascita"); // trasformo la data di MySQL in un oggetto // di tipo GregorianCalendar GregorianCalendar data = new GregorianCalendar(); data.setTime(dataNascita); // imposto l'attributo data dell'oggetto v v.setDataDiNascita(data); rs.close(); st.close(); //conn.close(); return v; } // end if } catch (SQLException e) { throw new SQLException("Impossibile eseguire la query o chiudere la connessione."); } // fine try-catch } // fine metodo autenticazione_3.pdf Pag. 7/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] /** * @return the conn */ public Connection getConn() { return conn; } /** * @param conn the conn to set */ public void setConn(Connection conn) { this.conn = conn; } } // fine classe DAO /* * Utente.java */ package it.itiscastelli.classi; import java.util.GregorianCalendar; /** * * @author maurizio */ public class Utente implements java.io.Serializable { private int id; private String username; private String password; private String luogoDiNascita; private GregorianCalendar dataDiNascita; /** Creates a new instance of Utente */ public Utente() { // } public Utente(String username, String password) { this.username=username; this.password=password; } autenticazione_3.pdf Pag. 8/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the username */ public String getUsername() { return username; } /** * @param username the username to set */ public void setUsername(String username) { this.username = username; } /** * @return the password */ public String getPassword() { return password; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } /** * @return the luogoDiNascita */ public String getLuogoDiNascita() { return luogoDiNascita; } autenticazione_3.pdf Pag. 9/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] /** * @param luogoDiNascita the luogoDiNascita to set */ public void setLuogoDiNascita(String luogoDiNascita) { this.luogoDiNascita = luogoDiNascita; } /** * @return the dataDiNascita */ public GregorianCalendar getDataDiNascita() { return dataDiNascita; } /** * @param dataDiNascita the dataDiNascita to set */ public void setDataDiNascita(GregorianCalendar dataDiNascita) { this.dataDiNascita = dataDiNascita; } } // fine classe Utente Riportiamo qui invece di seguito le pagine web: File index.jsp <%@page import="it.itiscastelli.classi.*" %> <%@page contentType="text/html" pageEncoding="ISO-8859-2"%> <!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-2"> <title>Prova MD5</title> <style> body, td, tr { font-family: Verdana, Arial, "Trebuchet MS"; font-size: 13px; } </style> </head> <body> <h3>Hello MD5!</h3> <% String messaggio = "pippo"; out.println( messaggio + "=" + MD5.md5(messaggio) ); %> </body></html> autenticazione_3.pdf Pag. 10/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] File login.jsp <%@page contentType="text/html" pageEncoding="ISO-8859-2"%> <!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-2"> <title>Pagina di login</title> <style> body, td, tr { font-family: Verdana, Arial, "Trebuchet MS"; font-size: 13px; } </style> </head> <body> <h3>Login utente</h3> <% // visualizza l'id di sessione per scopi di debugging out.println("<p>Session ID: "+session.getId()+"</p>"); if (session.getAttribute("utente")!=null) response.sendRedirect("riservata.jsp"); %> <form name="provaFrm" action="${request.requestURI}" method="post" > <p>Username <input type="text" name="usernameTxt" value="$ {param.usernameTxt}" /><br/> Password <input type="password" name="passwordTxt" value="$ {param.passwordTxt}" /><br/> <input type="submit" name="inviaBtn" value="Invia"/></p> </form> <% if (request.getParameter("inviaBtn")!=null) { it.itiscastelli.classi.DAO dao; try { dao=new it.itiscastelli.classi.DAO(); dao.openConn(); } catch(Exception e) { out.println(e.getMessage()); return; } String username = request.getParameter("usernameTxt"); String password = request.getParameter("passwordTxt"); it.itiscastelli.classi.Utente v=null; autenticazione_3.pdf Pag. 11/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] try { v = dao.cercaUtente(username, password); } catch (Exception e) { dao.closeConn(); out.println(e.getMessage()); return; } if (v!=null) { session.setAttribute("utente",v); response.sendRedirect("riservata.jsp"); } else { out.println("Accesso non consentito. Per cortesia, riprova a inserire username e password."); response.setHeader("Refresh","5; login.jsp"); } // fine if // chiudo la connessione dao.closeConn(); } %> </body> </html> File riservata.jsp <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <%@page import="java.util.GregorianCalendar" %> <%@page import="java.util.Calendar" %> <!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>Pagina riservata</title> <style> body, td, tr { font-family: Verdana, Arial, "Trebuchet MS"; font-size: 13px; } </style> </head> <body> <h3>Accesso riservato</h3> <% // visualizza l'id di sessione per scopi di debugging out.println("<p>Session ID: "+session.getId()+"</p>"); autenticazione_3.pdf Pag. 12/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] // se l'attributo utente non esiste if (session.getAttribute("utente")==null) { out.println("<p>Pagina riservata. Non puoi accedere a queste informazioni.</p>"); out.println("<p>Tra pochi secondi verrai rediretto verso la home page.</p>"); response.setHeader("Refresh","5; login.jsp"); } else { it.itiscastelli.classi.Utente v = (it.itiscastelli.classi.Utente) session.getAttribute("utente"); out.println("Benvenuto "+v.getUsername()+". Accesso consentito."); GregorianCalendar d = v.getDataDiNascita(); int giorno = d.get(Calendar.DAY_OF_MONTH); int mese = d.get(Calendar.MONTH)+1; int anno = d.get(Calendar.YEAR); String dataNascita = giorno+"/"+mese+"/"+anno; out.println("Sei nato il "+dataNascita+ " a "+v.getLuogoDiNascita()+"."); out.println("<p><a href='logout.jsp'>Logout</a></p>"); } %> </body> </html> File logout.jsp <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <!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>Pagina di logout</title> <style> body, td, tr { font-family: Verdana, Arial, "Trebuchet MS"; font-size: 13px; } </style> </head> <body> <h3>Pagina di logout</h3> <% // visualizza l'id di sessione per scopi di debugging out.println("<p>Session ID: "+session.getId()+"</p>"); // se l'attributo della sessione esiste e non è nullo if (session.getAttribute("utente")!=null) { it.itiscastelli.classi.Utente v = (it.itiscastelli.classi.Utente) session.getAttribute("utente"); autenticazione_3.pdf Pag. 13/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] out.println("Grazie "+v.getUsername()+" di aver usato l'applicativo."); session.removeAttribute("utente"); session.invalidate(); out.println("<p>Tra pochi secondi verrai rediretto verso la home page.</p>"); response.setHeader("Refresh","5; login.jsp"); } else { response.sendRedirect("login.jsp"); } %> </body></html> Vediamo in azione il nostro applicativo: Fig. 1 – Login con username e password errati autenticazione_3.pdf Pag. 14/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Fig. 2 – Messaggio di errore: accesso non consentito Fig. 3 – Dati corretti autenticazione_3.pdf Pag. 15/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Fig. 4 – Accesso consentito Fig. 5 – Logout dall'applicativo autenticazione_3.pdf Pag. 16/17 Cozzetto © Laboratorio di Sistemi Autenticazione utente mediante MySQL Jsp [Java] Fig. 6 – L'ID di sessione è cambiato autenticazione_3.pdf Pag. 17/17 Cozzetto ©