JSP – Caso di studio 2: myShop • Costruiamo un’applicazione web di una certa complessità. • Vogliamo realizzare un front-end (interfaccia verso gli utenti) di un sito che vende dei prodotti online. • Quindi le pagine principali saranno: – – – – – home page, catalogo dei prodotti disponibili, pagina per aggiungere un prodotto nel carrello, pagina per gestire il carrello (modifica quantità, ecc.), pagina per effettuare un ordine. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 1 Caratteristiche chiave e configurazione • Utilizziamo un database per generare dinamicamente le pagine dei prodotti. • Sfruttiamo il supporto per le sessioni in JSP per tener traccia del carrello (i.e., i prodotti selezionati) del cliente. • Cerchiamo di non ripetere lo stesso codice (XHTML e/o Java) in più pagine, utilizzando la direttiva include. include Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 2 Deployment su latoserver • Il codice è scaricabile da: mitel.dimi.uniud.it/med • Su latoserver.dimi.uniud.it scaricare il file .zip e decomprimerlo all'interno della directory servlets (che si trova dentro la propria home directory), spostando myShop.jar da /home/<nomeutente>/servlets/myShop/WEB-INF/lib in /home/<nome-utente>/servlets/WEB-INF/lib (dove risiede anche il file pwlsGallery.jar). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 3 Configurare sql.jsp • Controllare la stringa di connessione in modo che punti al database corretto: String stringaConnessione= "jdbc:mysql://localhost/myShop"; • Editare i valori delle variabili utenteSQL e passwordSQL impostandoli alla stringa vuota "". Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 4 Il flusso logico dell'applicazione index.jsp prodotti.jsp aggiorna.jsp carrello.jsp elimina.jsp svuota.jsp invia.jsp ordina.jsp Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 5 Architettura di myShop: pagine principali • index.jsp: home page del sito (punto di partenza); • prodotti.jsp: pagina con l’elenco dei prodotti ordinabili; • carrello.jsp: pagina per l’inserimento e la visualizzazione dei prodotti nel carrello; • ordina.jsp: pagina con il modulo d’ordine. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 6 Architettura di myShop: pagine ausiliarie • aggiorna.jsp: aggiorna le quantità dei prodotti presenti nel carrello; • elimina.jsp: elimina un singolo prodotto dal carrello; • svuota.jsp: svuota interamente il carrello. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 7 Architettura di myShop: file include • I file seguenti sono inclusi nelle pagine del sito per mezzo di opportune direttive include: – banner.jsp: contiene il banner del sito (con logo e data corrente); – footer.jsp: piè di pagina; – heading.jsp: intestazione (tag <head>); – menu.jsp: menu di navigazione nel sito. – sql.jsp: stringa di connessione, nome utente e password per accedere a MySQL, funzioni utili, caricamento del driver. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 8 Osservazioni sui file include • E’ possibile includere con la direttiva include sia file dinamici (.jsp) che statici (.html). • Anche se un file da includere contiene solo risorse statiche, risulta comunque preferibile utilizzare l’estensione dinamica jsp; infatti se in futuro si renderà necessario includere del codice in tale file, non sarà necessario modificare tutte le direttive che lo utilizzano per aggiornarne l’estensione. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 9 La base di dati (I) • Su latoserver.dimi.uniud.it è possibile utilizzare il database myshop di cui fanno parte le seguenti tabelle: – – – – prodotti, intestazioni_ordini, righe_ordini, utenti_backoffice. • Lo script per creare e “popolare” le tabelle (tramite il client a linea di comando mysql) è scaricabile da mitel.dimi.uniud.it/med. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 10 La base di dati (II) • • La tabella prodotti contiene 3 elementi visibili tramite la query seguente: [scagnetto@latoserver scagnetto]$ > mysql -u anonymous mysql> use myshop mysql> select * from prodotti; +----+-------------------+----------------------------------------------+-------+ | ID | Prodotto | Descrizione | Prezzo | +----+-------------------+----------------------------------------------+-------+ | 1 | Hard Disk 400 GB | 7200 giri, 16MB cache, Controller Serial Ata | 230 | | 2 | Monitor CRT | 17'' | 130 | | 3 | Modulo RAM 512 MB | Modulo SDRAM 512 MB PC-133 | 70 | +----+-------------------+----------------------------------------------+-------+ 3 rows in set (0.00 sec) Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 11 Il diagramma E-R Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 12 Le classi dell'applicazione • Gli oggetti fondamentali gestiti dall'applicazione web coincidono con le entità del modello E-R. • In più c'è una classe apposita per la gestione dei carrelli virtuali. virtuali • Il package formato da queste classi si chiama myShop. • Le classi contenute nel package sono le seguenti: – – – – – Prodotto, ProdottoSelezionato, Carrello, Ordine, Utente. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 13 Le classi Prodotto e ProdottoSelezionato • La classe Prodotto modella un prodotto del negozio virtuale. • Le sue proprietà sono: – id: un intero che identifica il prodotto in modo univoco (int); – nome: il nome del prodotto (String); – descrizione: una breve descrizione del prodotto (String); – prezzo: il prezzo unitario (double). • La classe ProdottoSelezionato modella un prodotto del negozio virtuale selezionato da un cliente ed inserito nel carrello. • Estende la classe Prodotto aggiungendo la proprietà quantitaSelezionata. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 14 La classe Carrello • Modella un “carrello” di un cliente del negozio virtuale. • Un oggetto di tipo Vector contiene tutti i prodotti selezionati. • I metodi forniti dalla classe sono i seguenti: – AggiungiProdotto: aggiunge un prodotto al carrello. – EliminaProdotto: elimina un prodotto dal carrello. – ModificaQuantita: modifica la quantità di un prodotto nel carrello. – Svuota: elimina tutti i prodotti nel carrello. – TrovaIndiceProdotto: restituisce l'indice del prodotto di cui si fornisce l'identificatore numerico. – TrovaProdotto: restituisce il prodotto di cui si fornisce l'indice (posizione nel carrello). – Vuoto: restituisce true se il carrello è vuoto, false altrimenti. – NumeroProdotti: restituisce il numero di prodotti contenuti nel carrello. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 15 La classe Ordine • Modella un ordine effettuato da un cliente del negozio virtuale. • Le sue proprietà sono: numero dell'ordine, data, nome, cognome ed indirizzo del cliente, modalità di pagamento e di consegna, spese di spedizione ed un oggetto di tipo Vector che contiene i singoli prodotti ordinati. • I metodi della classe sono i seguenti: – AggiungiRiga: aggiunge una nuova riga (prodotto ordinato) all'ordine. – TrovaRiga: restituisce il prodotto selezionato di cui si fornisce la posizione (numero di riga). – NumeroRighe: restituisce il numero di righe (prodotti ordinati) dell'ordine. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 16 La classe Utente • Modella un utente dell'area riservata (backoffice) del negozio virtuale. • Le sue proprietà sono: – username: il nome utente necessario per effettuare il login nell'area riservata (String); – password: la password necessaria per effettuare il login (String); – descrizione: una breve descrizione dell'utente (String). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 17 Il package myShop.jar • Per maggiore “pulizia” del codice inseriamo in un package apposito le classi utili a modellare gli oggetti maggiormente utilizzati nell’applicazione. • Tutte le pagine JSP che avranno bisogno di tali classi potranno utilizzarle utilizzando l’attributo import della direttiva page nel modo seguente: <%@ page import="myShop.*" %> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 18 Compilazione del package • Compilazione delle singole classi: javac Carrello.java Ordine.java Prodotto.java ProdottoSelezionato.java Utente.java • Creazione dell'archivio jar: jar cvf myShop.jar myShop/*.class • myShop.jar va copiato in WEB-INF/lib. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 19 Il problema degli apici (I) • Supponiamo di voler inserire un nuovo record nella tabella anagrafica (composta da tre campi: Nome, Cognome, DataNascita): INSERT INTO anagrafica (Nome, Cognome, DataNascita) VALUES ('Mario', 'Rossi', '1987-1005') • Le stringhe di caratteri (come Mario) devono essere racchiuse fra apici. • Quindi per inserire una stringa, al cui interno compaiono degli apici come valore di un campo, bisogna “codificarli” codificarli per evitare che l’interprete SQL “spezzi” spezzi la stringa in più parti con gli errori di sintassi che ne conseguirebbero. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 20 Il problema degli apici (II) • Gli apici in SQL vengono codificati raddoppiandoli: – es.: la stringa l'articolo viene codificata in l''articolo • In questo modo è possibile delimitare la stringa con gli apici senza problemi nella sintassi SQL: 'l''articolo' Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 21 Il problema degli apici (III) • In Java è sufficiente scrivere la funzione seguente per la codifica: String CodificaApici(String s) { String codifica=""; for(int i=0; i<s.length(); i++) if(s.charAt(i)=='\'') codifica+="''"; else codifica+=s.charAt(i); return codifica; } Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 22 Il problema degli apici: form di login (I) • In generale si chiede all'utente di inserire username e password. • Poi si costruisce una query del genere: "SELECT COUNT(*) FROM utenti WHERE username="+"'"+username+"' AND password='"+password+"'" dove username e password sono variabili che contengono quanto inserito dall'utente nei campi omonimi del form. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 23 Il problema degli apici: form di login (II) • Se il controllo per abilitare il login viene fatto discriminando sul conteggio (0=accesso negato, <>0 accesso consentito), è sufficiente digitare quanto segue nel campo username: ' OR TRUE OR '1=1 • Infatti la query diventa: SELECT COUNT(*) FROM utenti WHERE username='' OR TRUE OR '1=1' AND password='non importa' • La clausola WHERE a questo punto è banalmente verificata falsando il conteggio. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 24 index.jsp (I) La struttura di index.jsp è la stessa delle altre pagine principali: <%@ page import="java.util.Date" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/XHTML" xml:lang="it" lang="it"> <%@ include file="heading.jsp" %> <body> <table width="100%"> <tr> <td colspan="2" class="blubg"> <%@ include file="banner.jsp" %> </td> </tr> ... Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 25 index.jsp (II) <tr> <td width="10%" class="blubg"> <%@ include file="menu.jsp" %> </td> <td align="center" width="90%"> <div class="intestazione">Benvenuti su myShop!</div> </td> </tr> <tr> <td colspan="2"> <%@ include file="footer.jsp" %> </td> </tr> </table> </body> </html> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 26 Il file prodotti.jsp (I) ... <%try { Connection c = DriverManager.getConnection(stringaConnessione, utenteSQL, passwordSQL); Statement s = c.createStatement(); ResultSet r = s.executeQuery("SELECT * FROM prodotti"); %> ... <table align="center"> <tr> Parametri definiti in sql.jsp <td class="intestazione_tabella"> Prodotto </td> ... <td class="intestazione_tabella"> Inserisci nel carrello </td> </tr> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 27 Il file prodotti.jsp (II) <% NumberFormat fmt=NumberFormat.getInstance(Locale.ITALIAN); fmt.setMinimumFractionDigits(2); fmt.setMaximumFractionDigits(2); int riga=0; while(r.next()) { riga++; String stile=riga%2==0 ? "riga2_tabella" : "riga1_tabella";%> <tr> <form method="post" action="carrello.jsp"> <td class='<%= stile%>'><%= r.getString(2)%></td> <td class='<%= stile%>'><%= r.getString(3)%></td> <td class='<%= stile%>'><%= fmt.format(r.getFloat(4))%></td> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 28 Il file prodotti.jsp (III) <td class='<%= stile%>'> <input type="text" name="qt" value="1" size="2" /> </td> <td class='<%= stile%>'> <input type="submit" name="ordina" value="&gt;&gt;" /> <input type="hidden" name="pid" value="<%= r.getInt(1)%>" /> </td> </form> </tr> <%}%> </table> <%r.close(); s.close(); c.close(); } catch (SQLException e) { response.sendRedirect("errore.jsp"); return; }%> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 29 Il file carrello.jsp (I) • La pagina carrello.jsp ha una duplice funzionalità: – aggiungere un prodotto al carrello, – visualizzare il carrello. • Se richiamata con il parametro stampa impostato a 1, visualizza soltanto il carrello: carrello.jsp?stampa=1 • Altrimenti ricerca nei parametri passati attraverso la richiesta il prodotto da aggiungere. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 30 Il file carrello.jsp (II) • Il codice della pagina risulta semplice, grazie allo sforzo fatto in fase di modellazione delle entità fondamentali dell'applicazione. • Controllo del carrello vuoto: Carrello carrello=null; if(session.getAttribute("myShop.carrello")==null) { carrello=new Carrello(); session.setAttribute("myShop.carrello",carrello); } else carrello=(Carrello)session.getAttribute("myShop.carrello"); … boolean carrelloVuoto=carrello.Vuoto(); Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 31 Il file carrello.jsp (III) • Controllo della presenza di un prodotto nel carrello: Connection c=DriverManager.getConnection(stringaConnessione, utenteSQL, passwordSQL); Statement s=c.createStatement(); ResultSet r=s.executeQuery("SELECT Prodotto, Descrizione, Prezzo" +"FROM prodotti WHERE ID="+idProdotto); ID="+idProdotto if(r.next()) { Prodotto p=new Prodotto(Integer.parseInt(idProdotto), r.getString(1), r.getString(2), r.getFloat(3)); } if(carrello.TrovaIndiceProdotto(Integer.parseInt(idProdotto)) ==-1) carrello.AggiungiProdotto(p, qtProdotto); else carrello.ModificaQuantita(Integer.parseInt(idProdotto), qtProdotto); Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 32 Il file carrello.jsp (IV) ... for(; i<carrello.NumeroProdotti(); i++) { String stile=(i+1)%2==0 ? "riga2_tabella" : "riga1_tabella"; ProdottoSelezionato ps=carrello.TrovaProdotto(i); %> <tr> <td class='<%= stile%>'><%= ps.nome%></td> <td class='<%= stile%>'><%= ps.descrizione%></td> <td class='<%= stile%>'><%= fmt.format(ps.prezzo)%></td> <td class='<%= stile%>'> <input type="hidden" name="id<%= i%>" value="<%= ps.id%>"/> <input type="text" name="qt<%= i%>" value="<%= ps.quantitaSelezionata%>" size="2" /> </td> <td class='<%= stile%>'> <%= fmt.format(ps.prezzo*ps.quantitaSelezionata)%></td> <td class='<%= stile%>'> <a href="elimina.jsp?id=<%= ps.id%>"> <img src="images/cestino.gif" border="0" /></a></td> </tr> <% } %> ... Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 33 Localizzazione della valuta (I) • Per stampare le valute nel formato più appropriato per la località corrente (separatore decimale corretto ecc.), si può utilizzare il codice seguente: NumberFormat fmt = NumberFormat. getInstance(Locale.ITALIAN); fmt.setMinimumFractionDigits(2); fmt.setMaximumFractionDigits(2); • In questo modo gli importi in Euro verranno stampati con due cifre dopo il separatore decimale (la virgola). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 34 Localizzazione della valuta (II) • Grazie alle classi NumberFormat e Locale, utilizzate come esposto nel lucido precedente, è poi possibile formattare valori che rappresentano valuta nel modo seguente: output.write("Totale: "+ fmt.format(new Double(totale))+ " Euro\n\n\n\n"); Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 35 La pagina aggiorna.jsp (I) <%@ page import="myShop.*" %> <% int numeroRighe=0; try { numeroRighe = Integer.parseInt(request.getParameter("righe")); } catch (NullPointerException e) { numeroRighe=0; } catch (NumberFormatException e) { numeroRighe=0; } Carrello carrello=null; if(session.getAttribute("myShop.carrello")==null) carrello=new Carrello(); else carrello=(Carrello)session.getAttribute("myShop.carrello"); Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 36 La pagina aggiorna.jsp (II) try { for(int i=0;i<numeroRighe;i++) { String idProdotto= request.getParameter("id"+(new Integer(i)).toString()); int qtProdotto=0; try { qtProdotto = Integer.parseInt( request.getParameter("qt"+(new Integer(i)).toString())); } catch (NullPointerException e) { qtProdotto=0; } catch (NumberFormatException e) { qtProdotto=0; } carrello.ModificaQuantita(Integer.parseInt(idProdotto), qtProdotto); } } catch (Exception e) { response.sendRedirect("errore.jsp"); return; } response.sendRedirect("carrello.jsp?stampa=1"); %> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 37 La pagina elimina.jsp <%@ page import="myShop.*" %> <% String idProdotto=request.getParameter("id"); Carrello carrello=null; if(session.getAttribute("myShop.carrello")==null) carrello=new Carrello(); else carrello=(Carrello)session.getAttribute("myShop.carrello"); try { carrello.EliminaProdotto(Integer.parseInt(idProdotto)); } catch (Exception e) { response.sendRedirect("errore.jsp"); return; } response.sendRedirect("carrello.jsp?stampa=1"); %> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 38 La pagina svuota.jsp <%@ page import="myShop.*" %> <% Carrello carrello=null; if(session.getAttribute("myShop.carrello")==null) carrello=new Carrello(); else carrello=(Carrello)session.getAttribute("myShop.carrello"); carrello.Svuota(); response.sendRedirect("carrello.jsp?stampa=1"); %> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 39 Controllare i campi di un form (I) String String String String String nome = request.getParameter("nome"); cognome = request.getParameter("cognome"); indirizzo = request.getParameter("indirizzo"); pagamento = request.getParameter("pagamento"); consegna = request.getParameter("consegna"); // variabile booleana: true -> valori corretti, // false -> errore di compilazione. boolean formValido=!nome.trim().equals("") && !cognome.trim().equals("") && !indirizzo.trim().equals("") && !pagamento.trim().equals("-") && Controllo delle !consegna.trim().equals("-"); combo box Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 40 Controllare i campi di un form (II) <% if(formValido) { %> messaggio e/o codice per gestire i valori inviati (ad esempio registrando l’ordine) <% } else { %> messaggio d’errore <% } %> Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 41 La pagina d'errore • Siccome gli errori che avvengono a tempo di esecuzione nelle pagine JSP sollevano delle eccezioni che possono essere gestite con dei blocchi try−catch, è opportuno realizzare una pagina che presenti un messaggio d’errore comprensibile anche per l’utente del Web meno esperto e fare in modo che, in caso di errori, quest’ultimo venga rediretto a tale pagina. • Praticamente tutto il codice potenzialmente “pericoloso” della nostra applicazione è racchiuso in opportuni blocchi try ed il relativo blocco catch provvede a reindirizzare l’utente alla pagina errore.jsp. • Nel caso di un’applicazione reale in questo tipo di pagine è opportuno inserire anche un recapito di posta elettronica a cui rivolgersi in caso l’errore si ripeta frequentemente. • Un altro accorgimento utile è utilizzare le pagine di gestione degli errori per memorizzare in un file di log (oppure in una tabella di un db) db i dati tecnici dell’errore. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 42 Parte II: Transazioni ed Area Riservata • Consistenza dei dati: le transazioni • Area riservata: – Autenticazione degli utenti tramite form e database – Protezione dell'accesso per mezzo delle sessioni – Visualizzazione degli ordini Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 43 La memorizzazione degli ordini (I) • Il form della pagina ordina.jsp invia le informazioni alla pagina invia.jsp che si occupa di controllare la validità dei dati inseriti dall'utente e di registrare l'ordine nel database. • Viene recuperato un riferimento al carrello virtuale dalla sessione corrente per inserire l'ordine in tre fasi: – si recupera il numero d'ordine più alto presente nella tabella intestazioni_ordini, – si memorizza l'intestazione del nuovo ordine, usando come numero il valore estratto al passo precedente più uno, uno – tramite un ciclo for, per ogni elemento presente nel carrello virtuale si esegue una query che inserisce un nuovo record nella tabella righe_ordini usando lo stesso numero d'ordine dell'intestazione del passo precedente. • Alla fine si svuota il carrello e si procede a visualizzare un messaggio per l'utente che segnali la corretta terminazione dell'operazione. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 44 La memorizzazione degli ordini (II) • La procedura appena descritta appare lineare e intuitiva, ma bisogna tenere conto della concorrenza degli accessi. • Cosa succederebbe se due clienti effettuassero un ordine allo stesso istante? istante – Entrambe le richieste, accedendo alla tabella delle intestazioni degli ordini in istanti ravvicinati, ricaverebbero lo stesso valore come numero d'ordine più alto. – Entrambe le richieste così imposterebbero lo stesso numero d'ordine generando un errore al momento di inserire nel database le relative intestazioni. • Infatti, essendo il numero d'ordine (attributo ID) chiave primaria della tabella intestazioni_ordini, non possono esserci due record con lo stesso valore di ID. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 45 Possibili soluzioni • Per evitare questa situazione, si può operare in due modi: – impostare isThreadSafe=false nella direttiva page, – utilizzare il meccanismo delle transazioni. transazioni • La prima alternativa non dà garanzie assolute (Tomcat potrebbe decidere di creare più istanze in memoria della servlet corrispondente alla pagina JSP). • L'utilizzo delle transazioni invece è la soluzione ideale perché si appoggia su una tecnologia consolidata ed ampiamente utilizzata nell'ambito dei database. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 46 Transazioni • Il concetto di transazione è molto importante nell’utilizzo delle basi di dati. • Si deve pensare ad una transazione come ad un’ operazione atomica, atomica ovvero, ad un’operazione che viene eseguita senza interruzioni. • In ogni dato istante soltanto un processo/thread può accedere agli oggetti del database manipolati da una transazione (realizzando così una sorta di sezione critica). • La transazione è composta da diverse query: – se si verifica un errore durante l’esecuzione di una qualunque di tali query, viene effettuato un rollback, rollback annullando l’effetto di tutte le query precedenti che compongono la transazione; – se tutte le query vengono eseguite senza errori, allora viene effettuato un commit, commit scrivendo le modifiche nella base di dati. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 47 Utilizzo delle transazioni • Sequenza di operazioni per implementare una transazione: – Dopo aver aperto la connessione e creato uno statement c come al solito, si disabilita la modalità di funzionamento standard di JDBC (auto-commit mode) mode che considera ogni singola query SQL come una transazione che provoca l’immediato aggiornamento (commit) del database: c.setAutoCommit(false); – Si inizia la transazione: • Si eseguono le varie query che compongono la transazione. • Se non si verificano errori, si procede ad eseguire il commit, aggiornando il database: c.commit(); • In caso di errori si effettua il rollback, annullando l’effetto di tutte le query della transazione: c.rollback(); Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 48 Il codice per la memorizzazione dell’ordine (I) ... Connection c = DriverManager.getConnection( stringaConnessione, utenteSQL, passwordSQL); Statement s = c.createStatement(); try { c.setAutoCommit(false); ResultSet r = s.executeQuery("SELECT MAX(ID) FROM intestazioni_ordini"); int idOrdine=0; if(r.next()) { idOrdine=r.getInt(1); } r.close(); idOrdine++; s.executeUpdate("INSERT INTO intestazioni_ordini (ID, Data, "+ "Nome, Cognome, Indirizzo, Pagamento, Consegna, "+ "SpeseSpedizione) VALUES ("+idOrdine+", '"+ formatoData.format(new Date())+"','"+CodificaApici(nome)+ "','"+CodificaApici(cognome)+"','"+CodificaApici(indirizzo)+ "','"+CodificaApici(pagamento)+"','"+CodificaApici(consegna)+ "',"+contributoSpeseSpedizione+")"); ... Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 49 Il codice per la memorizzazione dell’ordine (II) ... for(int i=0; i<carrello.NumeroProdotti(); i++) { ProdottoSelezionato ps=carrello.TrovaProdotto(i); s.executeUpdate("INSERT INTO righe_ordini (IDOrdine, "+ "IDProdotto, Prezzo, Quantita) VALUES ("+idOrdine+","+ ps.id+","+ps.prezzo+","+ps.quantitaSelezionata+")"); } c.commit(); } catch(SQLException e) { c.rollback(); response.sendRedirect("errore.jsp"); return; } ... Nota: in MySQL il tipo delle tabelle che supportano le transazioni è InnoDB (non MyISAM). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 50 L’area riservata • Spesso vi è la necessità di proteggere delle pagine di un sito o applicazione dall’accesso indiscriminato (anonimo). • Tale obiettivo può essere raggiunto in almeno due modi: – configurando direttamente il server (e.g.: file .htaccess di Apache); – realizzando un meccanismo di autenticazione tramite codice lato server ed il supporto di cookie/sessioni e di un database (per memorizzare gli utenti). • Il secondo metodo citato è più flessibile, ma richiede un certo sforzo da parte del programmatore. • Ovviamente se non viene progettato ed implementato con cura, il meccanismo di autenticazione può diventare una seria backdoor per le pagine che si intende proteggere. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 51 Il form di autenticazione: backoffice.jsp • L’idea alla base di ogni meccanismo di autenticazione basato su tecnologie lato server è di discriminare fra utenti autorizzati e non, non controllando nella sessione (o in un cookie) la presenza di un determinato oggetto. • Memorizzeremo quindi nella sessione di un utente che specifichi correttamente le proprie credenziali d’accesso un oggetto myShop.backofficeUser di tipo Utente. • Il controllo delle suddette credenziali (nome utente e password) avverrà mediante la ricerca di una corrispondenza di valori nella tabella utenti_backoffice. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 52 Controllo iniziale e recupero dei parametri • Se l’oggetto myShop.backofficeUser è presente nella sessione, redirezioniamo il browser verso la pagina protetta ordini.jsp: if(session.getAttribute("myShop.backofficeUser")!=null) { response.sendRedirect("ordini.jsp"); return; } • Se invece l'utente non si è ancora autenticato, recuperiamo i parametri del form: String username=request.getParameter("user"); if(username==null) username=""; String password=request.getParameter("pwd"); if(password==null) password=""; Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 53 Controllo dei parametri del form di login boolean autenticato=false; try { Connection c = DriverManager.getConnection( stringaConnessione, utenteSQL, passwordSQL); Statement s = c.createStatement(); ResultSet r = s.executeQuery("SELECT COUNT(*) FROM "+ "utenti_backoffice WHERE Username='"+ CodificaApici(username)+ "' AND Password='"+ CodificaApici(password)+"'"); if(r.next()) { autenticato=r.getInt(1)==1; } Si noti l'utilizzo del metodo CodificaApici() s.close(); c.close(); } catch (SQLException e) { response.sendRedirect("errore.jsp"); return; } Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 54 Prepared Statement • Una soluzione più professionale al problema della codifica degli apici (e quindi all'iniezione di codice SQL potenzialmente pericoloso) è costituita dall'uso dei Prepared Statement. • Essenzialmente sono degli oggetti che rappresentano dei comandi SQL precompilati. • In tal modo possono essere eseguiti in modo ripetuto con efficienza. • http://java.sun.com/j2se/1.4.2/docs/api/java/sql/ PreparedStatement.html Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 55 Prepared Statement • Esempio d'uso: String username=request.getParameter("username"); String password=request.getParameter("password"); PreparedStatement pstmt = con.prepareStatement("SELECT COUNT(*) FROM utenti_backoffice WHERE username = ? AND password=?"); sostituisce la stringa contenuta nella pstmt.setString(1, username); variabile username al posto del primo ‘?’ pstmt.setString(2, password); ResultSet r=pstmt.executeQuery(); sostituisce la stringa contenuta nella variabile username al posto del secondo ‘?’ Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 56 Memorizzazione delle password • Nell'esempio relativo a MyShop le password degli utenti sono memorizzate “in chiaro” nella tabella utenti_backoffice. • Ciò è comodo nel caso le si dimentichi (basta una query per recuperarle). • Tuttavia tale comodità rappresenta anche un problema di sicurezza. • Le password andrebbero sempre memorizzate in forma codificata con una one-way function (MD5, SHA-1 ecc.). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 57 Funzioni One-Way • Sono delle funzioni f tali che: – il calcolo di y=f(x) (codifica) è molto efficiente; – Il calcolo inverso, ovvero, ricavare x da f(x) (decodifica) è computazionalmente proibitivo o impossibile; – è altamente improbabile che due valori x e x' diversi fra loro, abbiano gli stessi valori secondo f (ovvero, f(x)=f(x')). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 58 Modus operandi • Volendo sfruttare una one-way function, si opera come segue: – le password sono salvate in forma codificata nel database; – l'utente inserisce la propria password in chiaro nel form di autenticazione; – la password fornita dall'utente viene codificata ed il risultato della codifica viene confrontato con il valore memorizzato nel database; – se le due codifiche coincidono, l'utente è autenticato, altrimenti no. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 59 MD5 in Java import java.security.MessageDigest; import java.math.BigInteger; public class MD5Test { public static void main(String args[]) throws Exception { String password = "test"; MessageDigest m = MessageDigest.getInstance("MD5"); m.update(password.getBytes("UTF8")); byte s[] = m.digest(); BigInteger bigInt = new BigInteger(1, s); String result = bigInt.toString(16); System.out.println("Password: "+password); System.out.println("Password digest (MD5): "+result); } } Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 60 Impostazione dell’oggetto myShop.backofficeUser • Se le credenziali dell’utente hanno trovato riscontro positivo nella tabella utenti backoffice, allora si provvede ad impostare l’oggetto myShop.backofficeUser nella sessione corrente ed a redirigere il browser verso la pagina protetta: if(autenticato) { Utente u=new Utente(username, password, "Amministratore"); session.setAttribute("myShop.backofficeUser", u); response.sendRedirect("ordini.jsp"); return; } • Il flag autenticato viene utilizzato anche per determinare cosa inviare come risposta al browser (messaggio d’errore oppure form di login). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 61 La pagina protetta: ordini.jsp • La pagina ordini.jsp permette agli utenti autenticati di visualizzare una tabella con tutti gli ordini effettuati. • Inoltre, per ogni ordine, è possibile visualizzare il dettaglio dei prodotti ordinati. • Tale operazione si svolge richiamando, tramite un link, la stessa pagina con un parametro che specifica il numero d’ordine di cui visualizzare le righe. • Inizialmente si controlla se l’utente è autorizzato a visualizzare la pagina; in caso contrario lo si reindirizza al form di login: if(session.getAttribute("myShop.backofficeUser")==null) { response.sendRedirect("backoffice.jsp"); return; } Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 62 Proteggere le pagine riservate • Nel caso siano presenti più pagine riservate, il controllo visto precedentemente va effettuato in ognuna di esse. • Infatti non è possibile imporre un ordine di visita delle pagine ed un client potrebbe cercare di indirizzarla direttamente saltando il form di login. • Quindi sarebbe meglio includere il codice in un file con estensione jsp da includere in ogni pagina in cui ce ne sia bisogno. Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 63 ordini.jsp: recupero del parametro Si procede a recuperare l'eventuale numero d'ordine di cui bisogna visualizzare il dettaglio delle singole righe che lo compongono: String idOrdine=request.getParameter("id"); try { Integer.parseInt(idOrdine); } catch (NullPointerException e) { idOrdine="0"; } catch (NumberFormatException e) { idOrdine="0"; } Si noti che in caso di parametro assente (NullPointerException) o di errore di formato (NumberFormatException), la variabile idOrdine viene inizializzata alla stringa "0" (numero d'ordine non esistente). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 64 ordini.jsp: recupero dei dati (I) Recupero dell'intestazione degli ordini: Vector ordini=new Vector(); boolean nessunOrdine=true; ... Connection c = DriverManager.getConnection( stringaConnessione, utenteSQL, passwordSQL); Statement s = c.createStatement(); ResultSet r = s.executeQuery("SELECT ID, Data, Nome, Cognome, "+ "Indirizzo, Pagamento, Consegna, SpeseSpedizione FROM "+ "intestazioni_ordini ORDER BY Data DESC"); while(r.next()) { Ordine o=new Ordine(r.getInt(1), r.getDate(2), r.getString(3), r.getString(4), r.getString(5), r.getString(6), r.getString(7), r.getFloat(8)); ... Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 65 ordini.jsp: recupero dei dati (II) Recupero delle righe relative all'ordine corrente: ... Statement s2 = c.createStatement(); ResultSet r2=s2.executeQuery("SELECT P.ID, P.Prodotto, "+ "P.Descrizione, R.Prezzo, R.Quantita FROM prodotti AS P, "+ "righe_ordini AS R WHERE P.ID=R.IDProdotto AND R.IDOrdine="+ o.numero); while(r2.next()) { Prodotto p=new Prodotto(r2.getInt(1), r2.getString(2), r2.getString(3), r2.getFloat(4)); o.AggiungiRiga(p, r2.getInt(5)); } ... ordini.add(o); } Si noti come l'ordine corrente venga aggiunto alla collezione ordini soltanto quando siano state recuperate tutte le informazioni su di esso (righe comprese). Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 66 La pagina ordini.jsp Complementi di Tecnologie Web – M. Franceschet, V.Della Mea e I.Scagnetto, a.a. 2011/12 - 67