miércoles, 21 de abril de 2010

Jcaptcha



Ya hace algún tiempo se ha hecho famoso el hecho de que cuando intentamos ingresar información en un Contact us, enviar un SMS o crear una cuenta de email, el ver una imagen con letras. Muchas veces estas letras se encuentran decoradas con animales u otros dibujos a fin de hacerles un poco más difíciles de leer. Esto es utilizado para comprobar que es una persona quien se encuentra utilizando el computador y no otra computadora con algún script malicioso.

Esto me llamo la atención y pude determinar que existen muchas maneras de crear este tipo de controles en PHP y me pregunté que sucede con JAVA, entonces en la búsqueda de información en la Internet me encontré con una librería de Java llamada JCAPTCHA.

Aquí está un pequeño tutorial que explica el funcionamiento de JCAPTCHA, junto con Eclipse. Asumo que para usar este tutorial ustedes tengan configurado en Eclipse un servidor web (que en mi caso es Tomcat v6.0) y también los plugins necesarios para crear aplicaciones web dinámicas.

En primer lugar creamos un “Dynamic Web Project”.





Para este caso, llamé al proyecto web como Validador.

Una vez creado el proyecto, se tienen que colocar dos librerías en la carpeta lib de WEB-INF que se generó con el proyecto, estas librerías son jcaptcha-all-1.0-RC6.jar que es descargable de: http://forge.octo.com/jcaptcha/confluence/display/general/Home. Para esta primera librería descargue el zip, lo descomprimí y guarde el jar en la carpeta lib. La segunda librería es commons-collections-3.2.jar que es descargable de: http://www.ibiblio.org/maven/commons-collections/jars




Una vez hecho esto, se procede a crear una clase de Java. Esta clase pertenecerá para este ejemplo al paquete com.singleton. Esta clase es creada en base al patrón Singleton, para aquellos que no lo sepan Singleton es un patrón de instancia única que es usado para restringir la creación de objetos de una clase. Aquí llame a la clase como CaptchaSingleton.




package com.singleton;

import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;

import com.octo.captcha.service.image.ImageCaptchaService;

public class CaptchaSingleton {

private static ImageCaptchaService instancia = new DefaultManageableImageCaptchaService();

public static ImageCaptchaService getInstance() {

return instancia;

}

}



Una vez hecha esta clase, se tiene que crear un Servlet, que será el encargado de crear la respectiva imagen.

Aquí llamaré al Servlet como Imagen y lo colocaré en el paquete com.web


Como podemos ver en el código, el servlet devolverá un tipo image/jpeg, por medio de la invocación a las clases de la librería de Jcaptcha y a la clase Singleton antes creada.


package com.web;

import java.awt.image.BufferedImage;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import com.octo.captcha.service.CaptchaServiceException;

import com.singleton.CaptchaSingleton;

import com.sun.image.codec.jpeg.JPEGCodec;

import com.sun.image.codec.jpeg.JPEGImageEncoder;



public class Imagen extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

static final long serialVersionUID = 1L;

public void init(ServletConfig servletConfig) throws ServletException {

super.init(servletConfig);

}

protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {

byte[] captchaChallengeAsJpeg = null;

ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();

try {

String captchaId = httpServletRequest.getSession().getId();

BufferedImage challenge =

CaptchaSingleton.getInstance().getImageChallengeForID(captchaId,

httpServletRequest.getLocale());

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();

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();

}

}

Posteriormente tenemos que crear una página jsp para mostar la imagen y el respectivo campo TextField para la comprobación. Aquí la pagina se llamará pagina1 y se debe notar que en el src de la imagen llama al Servlet antes creado. También se agregan un text llamado datos y un botón de submit. El formulario llama al servlet Respuesta que lo crearemos en el siguiente paso.





<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<!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>

<form name="form1" method="post" action="Respuesta">

<img src="Imagen">

<input type='text' name='datos' value=''>

<input type='submit' value='validar'>

</form>

</body>

</html>


En este paso creamos el servlet Respuesta que será el encargado de decirnos si los códigos son iguales. Vale destacar que se creó la función comprobarImagen que utiliza la clase Singleton para comprobar que la imagen y lo introducido en el text de la página JSP sean iguales.









protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// TODO Auto-generated method stub

PrintWriter out = response.getWriter();

if(comprobarImagen(request)){

out.print("Coinciden");

}else{

out.print("No Coinciden");

}

}

private boolean comprobarImagen(HttpServletRequest request){

boolean respuesta =Boolean.FALSE;

String captchaId = request.getSession().getId();

String response = request.getParameter("datos");

try {

respuesta = CaptchaSingleton.getInstance().validateResponseForID(captchaId,

response);

} catch (CaptchaServiceException e) {

e.printStackTrace();

}

return respuesta;

}

Una vez hecho esto ejecutamos página1.jsp veremos que se nos muestra una imagen y procederemos a realizar la respectiva verificación ingresando las letras en la caja de texto y dando clic sobre el botón validar.







Gracias a estos pequeños pasos podemos entender el funcionamiento de JCaptcha y entender una de las maneras que podemos defendernos de bots maliciosos.

4 comentarios:

  1. Antes de nada, gracias por el tutorial ;) Para los novatos como yo viene super bien desgranado.

    Qué valores has puesto en el web.xml?? Has metido algún dato allí??

    ResponderEliminar
  2. Por fin he conseguido echarlo a andar, pero tengo una duda... como se podría hacer para que el mensaje de error o acierto saliese en la misma página, debajo del formulario con el captcha??
    Por casualidad has intentado hacerlo??

    ResponderEliminar
  3. Que tal Andy, perdón por la respuesta un poco tardia. Desde el servlet podrias usar un RequestDispatcher y un forward a la pagina jsp. Tambien en el request pondrias un atributo y por medio de Expression Language mostrarias el mensaje en la pagina JSP. Podrias revisar http://festintecnologico.blogspot.com/2010/08/uso-de-objetos-implicitos-con-el.html

    ResponderEliminar
  4. Gracias me has sacado de un gran apuro, está muy bien explicado.

    ResponderEliminar