
guide
Tutti i linguaggi per diventare uno sviluppatore di app per Android.
Compressione di dati, autenticazioni, criptazione agendo solo sul deployment descriptor delle applicazioni web
L’utilizzo dei filtri, nelle applicazioni Web, è stato promosso, a “standard de jure”, con l’avvento delle specifiche 2.3 delle Servlet, sebbene alcuni produttori di Application Server avessero già in precedenza fornito delle funzionalità simili, anche se differenti l’una dall’altra.
Supponiamo di aver scritto una Servlet che gestisca l’autenticazione degli utenti relativamente ad una certa applicazione Web. Probabilmente, avremo costruito la nostra servlet in modo che verifichi l’esistenza delle credenziali di accesso fornite dagli utenti, interrogando un DataBase e, in caso positivo, istanziando una nuova sessione per ogni utente riconosciuto.
Supponiamo, adesso, che la nostra applicazione Web necessiti di tenere traccia di tutti i tentativi di accesso, da parte degli utenti (sia quelli riconosciuti dal sistema che quelli errati o potenzialmente illeciti), a causa di un presunto tentativo di login con credenziali non autenticate.
La prima cosa che ci viene in mente è quella di modificare la nostra Servlet in modo che scriva da qualche parte tali informazioni. La modifica (semplice o meno che sia) implicherà, comunque, una nuova operazione di deployment sull’Application Server.
In futuro si potrebbe presentare la necessità di tenere traccia di ulteriori informazioni, come, ad esempio, il numero di accessi effettuati verso una particolare tabella del DB. Anche in questo caso, saremo costretti a rieditare la nostra servlet, apportare la modifica necessaria e rieseguire il deployment su server. Il rischio, a lungo andare, è quello di popolare il codice con della logica che vada ben oltre lo scopo fondamentale della nostra servlet, ovvero quello di accettare delle richieste e fornire delle risposte ai client.
Lo scenario appena descritto è uno dei classici esempi in cui l’utilizzo di un filtro casca a fagiolo. I filtri rappresentano un modo per fornire una funzionalità aggiuntiva a una applicazione Web.
La potenza dei filtri risiede nel fatto che essi consentono di cambiare il comportamento delle applicazioni Web agendo sul deployment descriptor e sollevando il programmatore dalla necessità di modificare ogni volta il codice (e rieffettuare il deployment dell’applicazione).
Ci sono svariate circostanze in cui può rivelarsi utile l’utilizzo dei filtri.
Volendo fare una stima possiamo dire che i filtri più diffusi, probabilmente, sono:
Altri tipi di filtri, comunemente utilizzati, rientrano nelle seguenti categorie:
Se si ritiene che la nostra applicazione Web debba fornire una specifica funzionalità che esuli da quelle elencate, nulla ci vieterà di costruirci un filtro ad hoc in cui incapsularne le specifiche.
Un altro vantaggio non trascurabile derivante dall’utilizzo dei filtri è che essi sono riutilizzabili facilmente anche da altre servlet.
Per utilizzare un filtro all’interno di un’applicazione Web sono necessarie due cose:
javax.servlet.Filter
Per prima cosa andiamo a scoprire quali sono i metodi definiti nell’interfaccia javax.servlet.Filter
:
void init(FilterConfig filterConfig)
: Viene invocato, dal web container, subito dopo la creazione dell’istanza di un filtro e appena prima della messa in servizio del filtro stesso.void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
: Viene invocato, dal web container, e contiene l’implementazione vera e propria del filtro.void destroy()
: Viene invocato, dal web container, per indicare ad un filtro il termine del suo ciclo di vita.Si nota una certa somiglianza tra l’interfaccia Filter e l’interfaccia Servlet. Tale somiglianza non è casuale ma rispecchia un comportamento comune per quanto riguarda il ciclo di vita di questi due componenti.
Quando viene creato un filtro, il container si occuperà di invocare il metodo init()
, all’interno del quale sarà possibile accedere ai parametri di inizializzazione forniti attraverso l’interfaccia javax.servlet.FilterConfig
. I metodi definiti da quest’ultima interfaccia sono i seguenti:
String getFilterName()
: Restituisce il nome del filtro in relazione a quello definito nel deployment descriptorServletContext getServletContext()
: Restituisce un riferimento al ServletContext del chiamanteString getInitParameter(String name)
: Restituisce una stringa contenente il valore del parametro di inizializzazione il cui nome è quello contenuto nel parametro name.Enumeration getInitParameterNames()
: Restituisce i nomi dei parametri di inizializzazione del filtro attraverso un Enumeration di oggetti di tipo stringa.Torniamo al ciclo di vita dei filtri. Per soddisfare le richieste pervenute, il container invoca il metodo doFilter()
e, al termine del ciclo di vita del filtro stesso, richiama il metodo destroy()
. Come è possibile osservare, il metodo doFilter()
definisce al suo interno un parametro chain
, di tipo FilterChain, che fornisce un riferimento alla catena di filtri a cui la richiesta iniziale dovrà essere sottoposta. Infatti, è possibile filtrare le richieste pervenute dai client con una sorta di catena di filtri (Figura 1) che prevede l’invocazione a cascata di tutti i filtri coinvolti nella catena stessa.
Naturalmente, nei casi più semplici, la catena potrà essere composta da un unico anello che richiamerà direttamente la servlet indicata nella richiesta del client.
L’unico metodo definito nell’interfaccia javax.servlet.FilterChain
è il seguente:
void doFilter(ServletRequest request, ServletResponse response)
: Richiama il successivo Filtro da invocare, nella catena di filtri definita dal parametro chain. Nel caso in cui il filtro chiamante sia l’ultimo, o l’unico, della catena allora verrà richiamata la risorsa posta al termine della catena stessa.
Dal punto di vista del codice, all’interno del metodo doFilter()
di un filtro, quando si esegue l’istruzione chain.doFilter()
, verrà richiamato il successivo filtro definito all’interno della catena. Il codice che precede tale invocazione verrà eseguito sempre prima di passare il controllo al successivo componente. Le istruzioni successive alla chain.doFilter()
si occupano (quando presenti) di processare la risposta ottenuta. La figura seguente illustra uno scenario possibile:
Il secondo passo necessario all’implementazione di un filtro è costituito dalle modifiche da apportare al deployment descriptor. In esso sarà necessario riportare le informazioni utili al container per capire se siano presenti dei filtri e, in caso affermativo, quali e come essi siano associati ai componenti web dell’applicazione.
I tag utilizzati per tale scopo sono due: <filter>
e <filter-mapping>
.
Listato 1. Forma di un elemento filter
<filter>
<icon>contiene il path verso un file di tipo icona</icon>
<filter-name>Il nome del filtro</filter-name>
<display-name>Il nome del filtro visualizzato dai tool di gestione</display-name>
<description>Una descrizione del filtro</description>
<filter-class>Il nome completo della classe del filtro</filter-class>
<init-param>
<param-name>il nome di un parametro di inizializzazione del filtro</param_name>
<param-value>il valore del parametro di inizializzazione</param-value>
</init-param>
</filter>
In realtà, soltanto due dei sotto elementi di <filter>
descritti sono obbligatori nel deployment descriptor: <filter-name>
e <filter-class>
. Se si fa uso del tag <init-param>
, però, diventano obbligatori anche <param-name>
e <param-value>
. Le informazioni contenute all’interno del tag <init-param>
saranno accessibili attraverso l’oggetto di tipo FilterConfig ricevuto in input dal metodo init()
del filtro.
Listato 2. Forma di un elemento filter-mapping
<filter-mapping>
<filter-name>Lo stesso nome utilizzato all’interno del tag filter</filter name>
<url-pattern>La URL del componente a cui applicare il filtro</url-pattern>
</filter-mapping>
oppure
Listato 3. Forma di un elemento filter-mapping per una servlet
<filter-mapping>
<filter-name> Lo stesso nome utilizzato all’interno del tag filter </filter name>
<servlet-name>la servlet a cui verrà applicator il filtro</servlet-name>
</filter-mapping>
È fondamentale che i tag presenti nel deployment descriptor seguano rigorosamente l’ordine specificato nel Document Type Definition (DTD). In particolare, tutti i tag di tipo <filter>
devono essere inseriti prima dei tag di tipo <filter-mapping>
. Questi ultimi, a loro volta, devono precedere i tag di tipo <servlet>
. Nel caso di filtri a catena da applicare ad una richiesta, sarà necessario specificare ogni filtro in un elemento di tipo <filter-mapping>
, tenendo presente che l’ordine in cui i filtri verranno applicati sarà lo stesso che verrà introdotto nel deployment descriptor. Ad esempio, se il deployment descriptor fosse costituito nel seguente modo:
Listato 4. Esempio di deployment descriptor
<filter-mapping>
<filter-name>FiltroX</filter name>
<servlet-name>MyServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>FiltroY</filter name>
<servlet-name>MyServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>FiltroZ</filter name>
<servlet-name>MyServlet</servlet-name>
</filter-mapping>
ogni richiesta verso la Servlet denominata Login sarebbe prima intercettata dal filtro FiltroX; Quest’ultimo richiamando il metodo chain.doFilter()
attiverà, in sequenza, il filtro FiltroY
che, a seguire, attiverà il filtro FiltroZ
. Soltanto all’invocazione di chain.doFilter()
da parte del filtro FiltroZ
verrà, finalmente, richiamata la Servlet MyServlet
.
Nella parte precedente dell’articolo abbiamo visto la teoria di funzionamento dei filtri. In questa parte, invece, vedremo un esempio pratico di funzionamento.
Per comprendere i concetti espressi finora creiamo una semplice applicazione Web basata su una servlet (FilteredLogin) la cui richiesta, proveniente da una pagina html di input (login.html), viene preventivamente filtrata da due filtri denominati rispettivamente FiltroA
e FiltroB
. Scopo di tale applicazione è semplicemente quello di mostrare l’ordine di esecuzione dei vari componenti che costituiscono un’applicazione Web soggetta a filtri, attraverso dei semplici messaggi di testo.
Il passo successivo (lasciato al lettore per esercizio) è quello di costruirsi dei filtri personalizzati che facciano qualcosa di più concreto.
Listato 5. login.html: pagina html iniziale
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=ISO-8859-1″ />
<title>Una Applicazione Thread Unsafe</title>
</head>
<body>
<h1>Login</h1>
<p>Inserire nei campi sottostanti la propria UserID e Password:</p>
<form action=”FilteredLogin” method=”POST”>
<p><input type=”text” name=”userID” length=”40″ /></p>
<p><input type=”password” name=”password” length=”40″ /></p>
<p><input type=”submit” value=”Invia” /></p>
</form>
</body>
</html>
Listato 6. FilteredLogin.java: Servlet soggetta ai filtri (Vedi codice completo)
…
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
System.out.println(“La Servlet sta processando il metodo doPost”);
String userID = request.getParameter(“userID”);
String password = request.getParameter(“password”);
response.setContentType(“text/html”);
PrintWriter writer = response.getWriter();
writer.println(“<html><body>”);
writer.println(“Benvenuto ” + userID + “!”);
writer.println(“</body></html>”);
writer.close();
}
…
Il primo filtro ad essere richiamato è il filtro denominato FiltroA
. All’interno del metodo doFilter()
è ben visibile la divisione del codice nei tre punti principali:
Listato 7. FiltroA.java: primo filtro (Vedi codice completo)
…
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
System.out.println(“Il filtro FiltroA ha ricevuto la richiesta dal client”);
System.out.println(“Il filtro FiltroA smista la richiesta al filtro FiltroB”);
try
{
chain.doFilter(request, response);
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println(“La servlet ha terminato la sua azione”);
System.out.println(“Il filtro FiltroA è pronto per processare la risposta ” +
“ricevuta dalla servlet”);
…
System.out.println(“Il filtro FiltroA è pronto per processare la ” +
“risposta ricevuta dal filtro FiltroB”);
…
Analoghe considerazioni valgono per il filtro FiltroB
. In questo caso, però, essendo l’ultimo della catena, la successiva invocazione sarà rivolta direttamente alla Servlet.
Listato 8. FiltroB.java: ultimo filtro della catena (Vedi codice completo)
…
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
System.out.println(“Inizio del metodo doFilter del filtro FiltroB”);
System.out.println(“Il client remoto è ” + request.getRemoteHost());
System.out.println(“La userID inserita è ” +
request.getParameter(“userID”));
System.out.println(“La password inserita è ” +
request.getParameter(“password”));
…
System.out.println(“La servlet ha terminato la sua azione”);
System.out.println(“Il filtro FiltroB ripassa il controllo al ” +
“filtro FiltroA”);
…
Non ci rimane che scrivere il deployment descriptor secondo le indicazioni osservate in precedenza:
Listato 9. Web.xml: deployment descriptor valido per Tomcat 5.5 (Vedi codice completo)
…
<filter>
<filter-name>FiltroA</filter-name>
<filter-class>web.FiltroA</filter-class>
</filter>
…
<filter-mapping>
<filter-name>FiltroB</filter-name>
<url-pattern>/FilteredLogin</url-pattern>
</filter-mapping>
<servlet>
<description></description>
<display-name>FilteredLogin</display-name>
<servlet-name>FilteredLogin</servlet-name>
<servlet-class>web.FilteredLogin</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FilteredLogin</servlet-name>
<url-pattern>/FilteredLogin</url-pattern>
</servlet-mapping>
…
Effettuiamo il deployment dell’applicazione e mandiamola in esecuzione. Inseriamo i dati richiesti dalla pagina html iniziale, clicchiamo sul pulsante “Invia” e andiamo a vedere la console di Java.
Se vuoi aggiornamenti su Filtri di Java per applicazioni Web inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
Simone Onofri ci spiega i pericoli insiti in HTML5.
Tutti i linguaggi per diventare uno sviluppatore di app per Android.
Come creare applicazioni per il Web con PHP e MySQL per il DBMS.
Tutte le principali tecnologie per diventare uno sviluppatore mobile per iOS.
I fondamentali per lo sviluppo di applicazioni multi piattaforma con Java.
Diventare degli esperti in tema di sicurezza delle applicazioni Java.
Usare Raspberry Pi e Arduino per avvicinarsi al mondo dei Maker e dell’IoT.
Le principali guide di HTML.it per diventare un esperto dei database NoSQL.
Ecco come i professionisti creano applicazioni per il Cloud con PHP.
Lo sviluppo professionale di applicazioni in PHP alla portata di tutti.
Come sviluppare applicazioni Web dinamiche con PHP e JavaScript.
Fare gli e-commerce developer con Magento, Prestashop e WooCommerce.
Realizzare applicazioni per il Web utilizzando i framework PHP.
Creare applicazioni PHP e gestire l’ambiente di sviluppo come un pro.
Percorso base per avvicinarsi al web design con un occhio al mobile.
Realizzare siti Web e Web application con WordPress a livello professionale.
Iniziamo ad utilizzare il framework per web application ZK, creando un semplice “Hello World” in ZK Studio.
Breve panoramica che illustra le principali caratteristiche delle servet 3.0, con esempi
UrlRewriteFilter: emulare mod_rewrite in Java
Impariamo a sviluppare applicazioni Java per i sistemi Embedded, piattaforme che a differenza delle tradizionali soluzioni general purpose vengono progettate per svolgere compiti specifici. Nel corso delle guida verranno fornite le nozioni necessarie per installare Oracle Java SE Embedded, scegliere il profilo e la JVM da utilizzare, configurare una JRE personalizzata, creare una prima App ed effettuarne il deploy