Captcha nelle applicazioni Java Web Based
Questo tutorial ci insegna ad utilizzare una libreria opensource per inserire nelle nostre applicazione i famosi “Captcha”
Introduzione
Ultimamente vi sarà sicuramente capitato di dovervi registrare su qualche sito online. Su un numero crescente di siti è possibile trovare un nuovo sistema di sicurezza per il login, ovvero una semplice immagine generata da pagine dinamiche in cui viene riportato una stringa random che l’utente deve riportare, per dare la certezza al sito che chi si sta registrando è un utente e non un bot. Un’immagine d’esempio è quella riportata qui di seguito
Questa tipologia di login chiaramente necessita di un engine che produce queste immagini a partire da testo random e se vi fate un giro sulla rete esistono diverse librerie opensource per diversi linguaggi che permettono di utilizzare questa tecnologia nel proprio sito.
Quella che ora noi prenderemo in esame con un breve tutorial introduttivo è JCaptcha, progetto opensource realizzato da Marc-Antoine Garrigue, Sébastien Brunot, Mathieu Gandin e Benoit Doumas.
Overview architetturale
Il progetto JCaptcha è stato concepito per facilitare l’estensione, l’integrazione e la configurazione.
- L’estensione riguarda la definizione e l’esecuzione di nuovi test. Questo è possibile attraverso il modulo core e l’interfaccia Captcha che definisce un test Captcha con un alto livello di astrazione
- L’integrazione riguarda come le applicazioni possono utilizzare il framework Captcha. Questo viene reso possibile attraverso dei moduli d’integrazione che permettono agli sviluppatori di aggiungere facilmente un nuovo modulo. Ogni modulo fornisce l’integrazione dell’implementazione di CaptchaService e della tecnologia scelta
- La configurazione riguarda invece i Captcha test. Può essere realizzata con un modulo engine e dal CaptchaFactories
- La configurazione riguarda anche CaptchaService e i moduli. Questo può essere fatto attraverso l’implementazione MBean che viene fornita.
Qui di seguito viene riportato un schema logico riguardante la struttura di JCaptcha
Passiamo ora a vedere come possiamo utilizzare JCaptcha nelle nostre applicazioni
Installazione
La versione presa in esame in questo articolo è la 1.0RC3 e per poter usufruire della libreria dobbiamo inserire nella directory WEB-INF/lib della nostra web application il jar jcaptcha-all.jar, che troviamo nella distribuzione bin, e il jar di Jakarta Commons Collection a partire dalla versione 2.1. In questo modo la nostra applicazione può utilizzare i servizi offerti da JCaptcha.
Al momento non è disponibile un repository Maven dove poter scaricare questa libreria, quindi per chiunque fosse interessato ad integrarlo all’interno di un proprio progetto consiglio di crearsi in locale le dipendenze ed inserirle manualmente nel repository.
Creazione di un CaptchaService
Un servizio Captcha deve essere utilizzato in modalità singleton nella nostra applicazione, ovvero deve rispettare un certo design pattern, il quale viene riproposto nella classe wrapper riportata di seguito che possiamo utilizzare nella nostra applicazione
import com.octo.captcha.service.image.ImageCaptchaService; import com.octo.captcha.service.image.DefaultManageableImageCaptchaService; public class CaptchaServiceSingleton { private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService(); public static ImageCaptchaService getInstance(){ return instance; } }
In questa classe non facciamo altro che definire staticamente un oggetto ImageCaptchaService e poi renderlo disponibile con un metodo get statico. In questo modo tutti quelli che richiameranno il servizio Captcha avranno lo stesso oggetto.
Servlet d’esempio
Ora dobbiamo avere sotto mano un primo esempio di questa libreria per capire poi come poterla integrare nelle nostre web application. Quella che viene proposta qui è una semplice Servlet nella quale andiamo a generare un immagine Captcha e utilizziamo l’id di sessione come numero per generarla.
Potevamo anche utilizzare qualcosa di diverso, come un semplice numero random, ma parlando di web application viene naturale pensare subito all’id di sessione. La prima cosa di cui abbiamo bisogno è una servlet che generi l’immagine Captcha a partire dalla sessione, Servlet che viene riportata qui di seguito
import com.octo.captcha.service.CaptchaServiceException; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; public class ImageCaptchaServlet extends HttpServlet { public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); } protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { byte[] captchaChallengeAsJpeg = null; // L'output stream che ci serve per renderizzare l'immagine Captcha in una JPEG ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); try { // Prendiamo l'id di sessione che utilizzeremo per generare l'immagine captcha. // Lo stesso id sarà utilizzato per validare la risposta String captchaId = httpServletRequest.getSession().getId(); BufferedImage challenge = CaptchaServiceSingleton.getInstance() .getImageChallengeForID(captchaId, httpServletRequest.getLocale()); // L'encoder JPEG JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream); jpegEncoder.encode(challenge); } catch (IllegalArgumentException e) { httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND); return; } catch (CaptchaServiceException e) { httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); // Mettiamo il risultato nella risposta httpServletResponse.setHeader("Cache-Control", "no-store"); httpServletResponse.setHeader("Pragma", "no-cache"); httpServletResponse.setDateHeader("Expires", 0); httpServletResponse.setContentType("image/jpeg"); ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream(); responseOutputStream.write(captchaChallengeAsJpeg); responseOutputStream.flush(); responseOutputStream.close(); } }
Ora abbiamo creato la Servlet che genera l’immagine. Prima di tutto inseriamo nel web.xml il riferimento a questa Servlet, dandogli il nome di “jcaptcha”. In questo modo la potremo richiamare utilizzando solo questo nome.
<servlet> <servlet-name>jcaptcha</servlet-name> <servlet-class>ImageCaptchaServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet> ..... ..... ..... <servlet-mapping> <servlet-name>jcaptcha</servlet-name> <url-pattern>/jcaptcha</url-pattern> </servlet-mapping>
Pagina di test
Ora dobbiamo scrivere una semplice pagina HTML nella quale richiamiamo la Servlet che utilizza JCaptcha per generare un’immagine.
<html> <head> <title></title> </head> <body> Inserisci il valore che vedi<br> <form action="verificaInformazione"> <img src="jcaptcha"> <input type='text' name='j_captcha_response' value=''> <input type="submit" value="Invia"> </form> </body> </html>
Quello che verrà visualizzato è una semplice pagina con un immagine, un form di input e un bottone per l’invio. Come già detto in questo caso l’immagine sarà generata in maniera random a partire dalla nostra sessione utente.
Servlet di processamento
Infine dobbiamo creare la servlet che verifica le informazioni che inseriamo. Nel precedente esempio di codice HTML abbiamo già inserito l’url verso il quale dirigere l’informazione che passiamo, ovvero verificaInformazione. Dovremo quindi creare una servlet che verifica quello che noi abbiamo inserito e che risponde in maniera coerente al nostro utente.
La verifica viene effettuata a partire dalla sessione utente e dal valore che noi abbiamo passato come parametro. Segue quindi il codice dell’applicazione
/* * VerificaServlet.java * * Created on 24 luglio 2006, 10.54 */ import java.io.*; import java.net.*; import com.octo.captcha.service.CaptchaServiceException; import javax.servlet.*; import javax.servlet.http.*; /** * * @author fpaparoni * @version */ public class VerificaServlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); Boolean isResponseCorrect =Boolean.FALSE; String captchaId = request.getSession().getId(); String jcaptchaResponse = request.getParameter("j_captcha_response"); try { isResponseCorrect = CaptchaServiceSingleton .getInstance() .validateResponseForID(captchaId,jcaptchaResponse); } catch (CaptchaServiceException e) { //Gestire il caso in cui la libreria //generi un problema } out.println("<html>"); out.println("<head>"); out.println("<title>Servlet VerificaServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Servlet VerificaServlet</h1><br>"); if (isResponseCorrect.booleanValue()) { out.println("Codice valido"); } else { out.println("Codice inserito non valido"); } out.println("</body>"); out.println("</html>"); out.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } public String getServletInfo() { return "Verifica informazioni JCaptcha"; } }
CaptchaServiceSingleton è la classe che verifica le informazioni che abbiamo inviato, utilizzando come già detto la sessione utente e il parametro della form che abbiamo riempito. In questo modo abbiamo un esempio completo che utilizza JCaptcha. Consultando la documentazione ufficiale è possibile vedere esempi di integrazione con piattaforme come Struts e Spring
Link
Documentazione su JCaptcha
Home di JCaptcha
Definizione di Captcha su Wikipedia
Ho provato ad utilizzare jcaptcha ma credo di avere un problema di configurazione. Quando deployo il progetto su tomcat integrato in eclipse juno
INFO: Initializing ProtocolHandler [“ajp-bio-8009”]
ago 01, 2013 5:12:40 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 611 ms
ago 01, 2013 5:12:40 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
ago 01, 2013 5:12:40 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.12
ago 01, 2013 5:12:40 PM org.apache.catalina.core.ApplicationContext log
INFO: Marking servlet jcaptcha as unavailable
ago 01, 2013 5:12:40 PM org.apache.catalina.core.StandardContext loadOnStartup
SEVERE: Servlet /TestJCaptcha threw load() exception
java.lang.ClassNotFoundException: com.octo.captcha.service.CaptchaServiceException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1676)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1521)
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:119)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1062)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1010)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4935)
at org.apache.catalina.core.StandardContext$3.call(StandardContext.java:5262)
at org.apache.catalina.core.StandardContext$3.call(StandardContext.java:5257)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
ago 01, 2013 5:12:40 PM org.apache.coyote.AbstractProtocolHandler start
INFO: Starting ProtocolHandler [“http-bio-8080”]
ago 01, 2013 5:12:40 PM org.apache.coyote.AbstractProtocolHandler start
INFO: Starting ProtocolHandler [“ajp-bio-8009”]
ago 01, 2013 5:12:40 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 321 ms
Utilizzo la jrs 7.0 e apache 7.0 con:
common-collections4-4.-alpha1.jar
common-logging-1.1.3.jar
jcaptcha-1.0-all.jar
come consigliato dal tutorial di jcaptcha.
da cosa potrebbe dipendere?
Hai provato a creare il WAR e fare il deploy in un Tomcat esterno? Se crei il WAR all’interno c’è il JAR di JCaptcha ?
Hai provato a creare il war e vedere se la libreria viene messa in WEB-INF/lib ?