Friday, July 8, 2011

A different web design

As a software engineer, I wish a web like this:

Yahoo logo

Very funny, short and hard to develop!!!


Monday, July 4, 2011

AJAX captcha - with jQuery and Java


Hi all,
In this article I show how to develop a jQuery captcha for your forms, using Java and servlets, fully ajaxified.

The result will be:


You can download the source code from here.


These kind of topics, typically are posted by php and web developers, who have to face with a common problem: spammers.

Well, don’t forget that this matter is for Java developers, too.
Captchas let to stop spammers from automatically and repeatidly registering to your site through forms and invading your database of contacts with fake registrations.

I admit that rarely I needed this kind of protection and this is not so very simple to enable with Java.
Furthermore, there is not a wide documentation available on the web and googling leads to several, different solutions.

Maybe jCaptcha is what you are looking for, but googling I found a wonderful jQuery plugin, very user-friendly: sexy-captcha.

It is a drag and drop captcha, full-ajaxified, and very user-friendly (read as “idiot proof”).
For a demo, please visit this page from the plugin’s author: DEMO

Let’s start with code!!!!

The most significant work for me about this was to adapt PHP code to Java code, then I clean code using some design pattern more adequate for this application.

Technologies involved:
  • JSP;
  • Servlet;
  • JSTL;
  • JRE6;
  • Apache Tomcat 7 (but any java application server could be used);
  • jquery and sexy-captcha plugin.


In this case I prefer to use simple and plain Java solution, avoiding frameworks, in order to show only how to perform an AJAX call to check and refresh captcha submission.
Anyway, it is simple to adapt this code to whatever java frameworks you prefer.

As IDE for development I used Eclipse.
First of all create a new Web Dynamic Project, as usual, within Eclipse.
Call it: captcha.
I use default configurations, so jsp will be by default under WebContent folder.

Then we need to import JSTL library that will perform JSP dynamic renderization
You can download them from here.

The configuration is quite stright-forward:
  • Import them by right clicking on project --> properties --> Java build path --> tab: Libraries --> Add External Jars.
  • Additional, mandatory step: copy the jars into /WEB-INF/lib


Now we have the Eclipse project set up and ready to code.

Let’s begin from JSP.

Under WebContent, create a new jsp called index.jsp (that by default is the welcome-file in the WEB.xml).

index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<!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>Captcha</title>
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/sexy-captcha/styles.css" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery-ui-1.7.2.custom.min.js"></script>
<script type="text/javascript" src="js/jquery.sexy-captcha-0.1.js"></script>
<script>
 $("document").ready(function() {
  $('.myCaptcha').sexyCaptcha('CaptchaServlet');

 });
</script>
</head>
<body>
 <form id="test-form" action="">
  <div class="myCaptcha"></div>
 </form>
</body>
</html>

As you can see there are some javascript libraries imported. I suggest to download the pack from sexy-captcha blog.

Then unpack it and copy the folders and files under WebContent folder of our Eclipse project.

Servlet.

Add a new servlet to project:
right click on project →  new →  Servlet → package name: it.nicogiangregorio.servlets and servlet name: CaptchaServlet.java

Then type the following code:
-------
package it.nicogiangregorio.servlets;

import it.nicogiangregorio.core.CaptchaContext;
import it.nicogiangregorio.core.impl.CaptchaRefreshAction;
import it.nicogiangregorio.core.impl.CaptchaVerifyAction;
import it.nicogiangregorio.utils.WebConstants;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class CaptchaServlet
 */
@WebServlet("/CaptchaServlet")
public class CaptchaServlet extends HttpServlet {

 /**
  * 
  */
 private static final long serialVersionUID = -243950967076586170L;

 /**
  * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
  *      response)
  */
 protected void doPost(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {

  String forwardJsp = null;
  String action = request.getParameter(WebConstants.PARAM_ACTION);

  CaptchaContext captchaCtx = new CaptchaContext();

  if (WebConstants.ACTION_VERIFY.equals(action)) {

   captchaCtx.setCaptchaAction(new CaptchaVerifyAction());

  } else if (WebConstants.ACTION_REFRESH.equals(action)) {

   captchaCtx.setCaptchaAction(new CaptchaRefreshAction());

  } else {
   System.out.println("Undefined behaviour due to unexpected action.");
  }

  forwardJsp = captchaCtx.processCaptcha(request, response);
  getServletContext().getRequestDispatcher(forwardJsp).forward(request,
    response);
 }
}

The doPost() method let to perform to actions:
  • VERIFY action, to check captcha submitted by user;
  • REFRESH action, to refresh captcha question;

Elaboration is done by classes with some business logic. I’ll show them in a while, again.
And finally redirects output to returned jsp.

I use a bunch of constants all stored into a Java class.


WebConstants.java
-------
package it.nicogiangregorio.utils;

/**
 * 
 * Constants class
 * 
 * @author Nico Giangregorio
 * 
 */
public class WebConstants {
 public static final String NICK_DEMO = "nick_demo";
 public static final String PARAM_ACTION = "action";
 public static final String ACTION_VERIFY = "verify";
 public static final String ACTION_REFRESH = "refresh";
 public static final String ATTR_CAPTCHA_ANSWER = "captchaAnswer";
 public static final String ATTR_CAPTCHA_CODES = "captchaCodes";
 public static final String VERIFY_RESULT = "VerifyResult";
 public static final String PARAM_CAPTCHA = "captcha";
 public static final String PARAM_CAPTCHA_SUBSTR = "draggable_";
 public static final String VERIFY_RESULT_SUCCESS = "success";
 public static final String VERIFY_RESULT_FAILED = "failed";
 public static final String VERIFY_FORWARD_JSP = "/verify.jsp";
 public static final String REFRESH_FORWARD_JSP = "/refresh.jsp";
 public static final String ERROR_FORWARD_JSP = "/error.jsp";
 public static final String ATTR_CAPTCHA_IMAGES = "captchaImages";
 public static final String ATTR_RIGHT_ANSWER = "rightAnswer";

 // Enforce non-istantiability
 private WebConstants() {
 }
}

Note: you can use static import, here I avoided it due to readability.
Now let’s what business logic does.
First of all, the application can do 2 simple things:
  • Verify the captcha;
  • Refresh the captcha;
So, our CaptchaServlet can perform two different actions, depending on what the user wants and finally forwarding the result of this computation to two different JSP, rendering result that will be the AJAX response.

In order to achieve this goal, I think it is better to implement the Strategy design pattern.
With this pattern, the application choose at runtime which algorithm adopt to provide computation required by user.

The strategy interface, package it.nicogiangregorio.core and interface ICaptchaAction.java:
package it.nicogiangregorio.core;

-----
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CaptchaContext {
 private ICaptchaAction captchaAction;

 public void setCaptchaAction(ICaptchaAction captchaAction) {
  this.captchaAction = captchaAction;
 }

 public String processCaptcha(HttpServletRequest request,
   HttpServletResponse response) {
  return captchaAction.process(request, response);
 }
}

The two possible strategies for captcha app are two implementation of this interface.
Let’s start with strategy related to refresh action.

REFRESH.

Refreshing allows to user, to generate a new question for him.
CaptchaRefreshAction.java

-----
package it.nicogiangregorio.core.impl;

import it.nicogiangregorio.core.ICaptchaAction;
import it.nicogiangregorio.utils.CaptchaEnum;
import it.nicogiangregorio.utils.CaptchaGenerator;
import it.nicogiangregorio.utils.WebConstants;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Strategy for Refreshing action: Reset captcha order and right answer to
 * captcha question, then forward to correct jsp otherwise forward to a courtesy
 * jsp
 * 
 * @author Nico Giangregorio
 * 
 */
public class CaptchaRefreshAction implements ICaptchaAction {

 @Override
 public String process(HttpServletRequest request,
   HttpServletResponse response) {

  Map<CaptchaEnum, String> captchaCodes = new HashMap<CaptchaEnum, String>();

  try {
   captchaCodes.put(CaptchaEnum.STAR,
     CaptchaGenerator.createCaptchaCodes());
   captchaCodes.put(CaptchaEnum.HEART,
     CaptchaGenerator.createCaptchaCodes());
   captchaCodes.put(CaptchaEnum.BWM,
     CaptchaGenerator.createCaptchaCodes());
   captchaCodes.put(CaptchaEnum.DIAMOND,
     CaptchaGenerator.createCaptchaCodes());

  } catch (IllegalStateException e) {
   return WebConstants.ERROR_FORWARD_JSP;
  }

  int index = new Random().nextInt(captchaCodes.size());
  CaptchaEnum rightAnswer = CaptchaEnum.values()[index];

  request.getSession().setAttribute(WebConstants.ATTR_CAPTCHA_ANSWER,
    captchaCodes.get(rightAnswer));

  request.getSession().setAttribute(WebConstants.ATTR_RIGHT_ANSWER,
    rightAnswer);

  request.getSession().setAttribute(WebConstants.ATTR_CAPTCHA_CODES,
    captchaCodes);

  return WebConstants.REFRESH_FORWARD_JSP;
 }
}

It simply creates a map of images available for captchas and associates for each image a random generated identifier.

The list of images and other CSS data associated with each of them is stored into CaptchaEnum.
Then it elects one the 4 captcha available that will be the “right answer”.

At the end it sends the attributes with results of elaboration to HTTP session.
Please, before writing your own custom implementation remember that object in HttpSession should be serializable as servlet specification requires.

In our case, the enum is CaptchaEnum.java:
----------

package it.nicogiangregorio.utils;

/**
 * Associate CSS info to a given image and enumerate them. This is immutable
 * andserializable. It strongly replaces a bean
 * 
 * @author Nico Giangregorio
 * 
 */
public enum CaptchaEnum {
 STAR("-120px", "-3px", "-120px", "-66px"), HEART("0", "-3px", "0px",
   "-66px"), BWM("-56px", "-3px", "-56px", "-66px"), DIAMOND("-185px",
   "-3px", "-185px", "-66px");

 private final String onTop;
 private final String onLeft;
 private final String offTop;
 private final String offLeft;

 CaptchaEnum(String onTop, String onLeft, String offTop, String offLeft) {
  this.onTop = onTop;
  this.onLeft = onLeft;
  this.offTop = offTop;
  this.offLeft = offLeft;
 }

 public String getOnTop() {
  return onTop;
 }

 public String getOnLeft() {
  return onLeft;
 }

 public String getOffTop() {
  return offTop;
 }

 public String getOffLeft() {
  return offLeft;
 }

}


As every enum it is serializable and immutable, so we don't have to be worried about concurrency and serialization.



The classes that generates this random identifiers is:
package:
it.nicogiangregorio.utils

class:
CaptchaGenerator.java
----------
package it.nicogiangregorio.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import sun.misc.BASE64Encoder;

/**
 * Helper class to generate random code to associate with captcha
 * 
 * @author Nico Giangregorio
 * 
 */
public class CaptchaGenerator {

 private CaptchaGenerator() {
 }

 public static String createCaptchaCodes() {

  MessageDigest digest = null;
  byte[] result = {};

  SecureRandom rand = new SecureRandom();
  BASE64Encoder encoderToBase64 = new BASE64Encoder();

  try {

   digest = MessageDigest.getInstance("SHA-256");
   digest.update(WebConstants.NICK_DEMO.getBytes());

  } catch (NoSuchAlgorithmException e) {
   throw new IllegalStateException(e);
  }

  String randString = "" + rand.nextDouble();
  result = digest.digest(randString.getBytes());

  return encoderToBase64.encode(result);
 }
}


starting from a random generated number from SecureRandom and with a Salt it creates an hashed string, using SHA-256.
It is a singleton..

And now..

VERIFY

The verify strategy checks if user submitted the correct captcha answer.

package:
it.nicogiangregorio.core.impl

class:
CaptchaVerifyAction.java

-----
package it.nicogiangregorio.core.impl;

import it.nicogiangregorio.core.ICaptchaAction;
import it.nicogiangregorio.utils.WebConstants;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CaptchaVerifyAction implements ICaptchaAction, WebConstants {

 @Override
 public String process(HttpServletRequest request,
   HttpServletResponse response) {
  String hashCaptcha = request.getParameter(PARAM_CAPTCHA).substring(
    PARAM_CAPTCHA_SUBSTR.length());
  String captchaAnswer = (String) request.getSession().getAttribute(
    ATTR_CAPTCHA_ANSWER);
  String result;

  if (captchaAnswer.equals(hashCaptcha))
   
   result = VERIFY_RESULT_SUCCESS;
  else {
   
   request.getSession().setAttribute(ATTR_CAPTCHA_CODES, null);
   request.getSession().setAttribute(ATTR_CAPTCHA_ANSWER, null);
   result = VERIFY_RESULT_FAILED;
  }

  request.getSession().setAttribute(VERIFY_RESULT, result);
  return VERIFY_FORWARD_JSP;
 }
}

Very simple and straight-forward.

Next we need to manage the correct strategy, with a context class:

package:
it.nicogiangregorio.core

class:
CaptchaContext
-------
package it.nicogiangregorio.core;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CaptchaContext {
 private ICaptchaAction captchaAction;

 public void setCaptchaAction(ICaptchaAction captchaAction) {
  this.captchaAction = captchaAction;
 }

 public String processCaptcha(HttpServletRequest request,
   HttpServletResponse response) {
  return captchaAction.process(request, response);
 }
}

It lets the application to select which strategy to use and finally performs processing.
Note: it returns what single strategies return, i.e. the right JSP to forward.

Each strategy and\or action, need to forward results of elaboration to one dedicated JSP, so create two new jsp:

verify.jsp
refresh.jsp

The names are self-explanatory.

This time we begin with (under WebContent)
verify.jsp:
-----
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8" import="it.nicogiangregorio.utils.WebConstants"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="VERIFY_RESULT"
 value="<%=request.getSession().getAttribute(WebConstants.VERIFY_RESULT) %>"></c:set>
 <c:set var="VERIFY_SUCCESS"
 value="success"></c:set>
<c:choose>
<c:when test="${VERIFY_RESULT eq VERIFY_SUCCESS }">
 {'status': 'success'}
</c:when>
<c:otherwise>
 {'status': 'error'}
</c:otherwise> 
</c:choose>
We generate a JSON output that is the response to JQuery sezy-captcha plugin. Next the jsp returning the updated html frame with captcha:
refresh.jsp

----
<%@page import="it.nicogiangregorio.utils.CaptchaEnum"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8" import="it.nicogiangregorio.utils.WebConstants"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>










It takes attributes from session and uses them to render a new snippet of HTML with refreshed captcha values.

That’s all.

Here our amazing captcha:



Download source code from github.



UPDATE: I edited some code on github and consequently this post. Now everything looks better and the app is 1.5+ compliant.



References: