Voici une application capable de faire la difference entre un humain et un bot. Les formulaires d’inscription ont souvent besoin de ce genre d’outils afin de securiser les serveurs. l’idée est de generer une image contenant un texte, et de le comparer à une valeur saisie par l’utilisateur.
Il est en effet très difficile de faire un script capable de passer ce genre de securité simple à mettre en oeuvre.

Voici un exemple d’image générée par notre programme :

Voici le principe de fonctionnement :

Pour fonctionner l’application est composée de :

    	
  • Une servlet permettant de realiser l'image dynamique et d'écrire en session sa valeur.
  • Une JSP de validation comparant la valeur en session à la valeur soumise via le formulaire.

Mon exemple tourne actuellement sur une architécture TomCat 5.5.7, JDK 5.0.

1) Le code de ma servlet :

/* * Created on 28 mars 2005 * */
package com.utils.dynimage;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.util.*;
import java.awt.*;
import java.io.*;
import java.awt.image.*;
import java.awt.font.*;
import javax.imageio.*;
import java.awt.geom.*;

/** * @author Eric * */
public class GetDynImage extends HttpServlet {

	public void init() throws ServletException {
		//System.out.println("Servlet started");
	}

	public void destroy() {
		//System.out.println("Servlet is stopping now");
	}

	protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
		doGet(arg0, arg1);
	}

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

		String text = getRandomText();

		// Sauvegarde de la string en session
		HttpSession session = request.getSession(true);
		session.setAttribute("dynImageTxt", text);
		String font_file = getServletContext().getRealPath ("WEB-INF/ttf/acmesa.ttf" );
		float size = 50.0f;
		Color background = Color.white;
		Color color = Color.black;

		try {
			Font font = Font.createFont(Font.TRUETYPE_FONT, new FileInputStream(font_file));
			font = font.deriveFont(size);
			BufferedImage buffer = new       BufferedImage(1,1,BufferedImage.TYPE_INT_RGB);
			Graphics2D g2 = buffer.createGraphics();
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,       RenderingHints.VALUE_ANTIALIAS_ON);
			FontRenderContext fc = g2.getFontRenderContext();
			Rectangle2D bounds = font.getStringBounds(text,fc);

			// Calcul de la taille du texte
			int width = (int) bounds.getWidth();
			int height = (int) bounds.getHeight();

			// Preparation de la sortie
			buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			g2 = buffer.createGraphics();
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,       RenderingHints.VALUE_ANTIALIAS_ON);
			g2.setFont(font);
			g2.drawString(text,0,(int)-bounds.getY());

			// Set content type et get de l'output stream
			response.setContentType("image/jpeg");
			OutputStream os = response.getOutputStream();

			// Output en PNG de l'image
			ImageIO.write(buffer, "jpg", os);
			os.close();

		} catch (Exception e) {
			// Traitement Erreur
		}
	}

	/***
	*
	* Method used to get a random UpperCase text of 5 letters.
	*
	* @return	String text.
	*/
	private String getRandomText () {

		// Génération d'un texte aléatoire
		int length = 5;
		char[] array = new char[ length ];
		char[] chars = new char[26];

		//for (int i = 0; i < 26; i ++) {chars[i] = (char) (97 + i);chars[i + 26] = (char) (65 + i);}

		// Creation d'une table avec les majuscules de l'alphabet.
		for (int i = 0; i < 26; i ++) {
			chars[i] = (char) (97 + i);
		}
		Random random = new Random ();

		for (int i = 0; i < length; i ++) {
			array[i] = chars[random.nextInt (chars.length)];
		}

		// Parametres par défaut
		return new String (array);

	}
}

2) La vérification de la valeur :

// Lecture de la session, de l'attribut captcha
HttpSession session = request.getSession(true);
String value = (String) session.getAttribute("dynImageTxt");

// lecture de la valeur du champs nommé imgValue du formulaire de saisie.
String formValue = request.getParameter("imgValue");

if (formValue.toLowerCase().equals(value.toLowerCase())) {
	// Valeur exacte !!!!
} else {
	// Valeur fausse !!!!
}

Attention : Si votre serveur est positionné derriere un load-balancer, vous devez prendre garde à le configurer de sorte que l’utilisateur tombe systématiquement sur la même machine, dans le cas contraire, la valeur en session ne correspondrait a rien.