Java CAPTCHA w/ JCaptcha


When looking for a good Java CAPTCHA library, many people just suggested using ReCAPTCHA from Google.  A few online posts discouraged using ReCAPTCHA because it has shown swear words at times and has the possibility of being down which would also bring your own site down.  I like my Java Web Apps to have as few dependencies as possible, so I looked for a self-contained solution.  JCaptcha had everything I needed and was easy to integrate.  It was nice to be able to customize what kind of CAPTCHA words to use and what they would look like.

First off, add the dependency with Maven or as a Jar:

<dependency>
     <groupId>com.octo.captcha</groupId>
     <artifactId>jcaptcha</artifactId>
     <version>1.0</version>
 </dependency>

Add a custom engine to make it look how you want:

public class CustomCaptchaEngine extends ListImageCaptchaEngine {

     protected void buildInitialFactories() {

        com.jhlabs.image.WaterFilter water = new com.jhlabs.image.WaterFilter();
        water.setAmplitude(2d);
        water.setAntialias(true);
        water.setPhase(20d);
        water.setWavelength(70d);

        ImageDeformation backDef = new ImageDeformationByFilters(new ImageFilter[]{});
        ImageDeformation textDef = new ImageDeformationByFilters(new ImageFilter[]{});
        ImageDeformation postDef = new ImageDeformationByFilters(new ImageFilter[]{water});

        com.octo.captcha.component.word.wordgenerator.WordGenerator dictionaryWords = new com.octo.captcha.component.word.wordgenerator.ComposeDictionaryWordGenerator(
        new com.octo.captcha.component.word.FileDictionary("toddlist"));

        TextPaster randomPaster = new DecoratedRandomTextPaster(new Integer(6), new Integer(7), new SingleColorGenerator(Color.black), new TextDecorator[]{
        new BaffleTextDecorator(new Integer(1), Color.white)});
        BackgroundGenerator back = new UniColorBackgroundGenerator(new Integer(200), new Integer(75), Color.white);

        FontGenerator shearedFont = new RandomFontGenerator(new Integer(30), new Integer(35));

        com.octo.captcha.component.image.wordtoimage.WordToImage word2image;
        word2image = new DeformedComposedWordToImage(shearedFont, back, randomPaster, backDef, textDef, postDef);

        this.addFactory(new com.octo.captcha.image.gimpy.GimpyFactory(dictionaryWords, word2image));
    }
}

Then add an endpoint to serve up a BufferedImage.  I used Jersey for this:

public static ImageCaptchaService captchaService = new DefaultManageableImageCaptchaService(new FastHashMapCaptchaStore(), new CustomCaptchaEngine(), 180, 100000, 75000);

@Path("captcha.jpg")
@Produces("image/jpeg")
@GET
public Response lookupCategorization(@Context HttpServletRequest httpRequest) {

      BufferedImage bi = captchaService.getImageChallengeForID(httpRequest.getSession(true).getId());

 return Response.ok(bi)
      .header("Expires", 0)
      .header("Cache-Control", "no-store, no-cache, must-revalidate")
      .header("Pragma", "no-cache")
      .build();
 }

Add a validation method:

public static boolean validateResponse(HttpServletRequest request, String userCaptchaResponse) {
     if (request.getSession(false) == null) {
         return false;
     }
     boolean validResponse = false;
     try {
        validResponse = captchaService.validateResponseForID(request.getSession().getId(), userCaptchaResponse);
     } catch (CaptchaServiceException e) {}
     return validResponse;
}

Add a validation endpoint:

@Path("validate")
@Produces(MediaType.APPLICATION_JSON)
@POST
public Response validate(@Context HttpServletRequest httpRequest, @FormParam("captcha") String captcha) {

    if(validateResponse(httpRequest, captcha)) {
        ...
    } else {
        ...
    }
}

Add HTML form fields for CAPTCHA:

  <img class="captchaimage" src="captcha.jpg" />
  <input type="text" class="captchaText" autocomplete="off" name="captcha" /> 
  <a href="javascript: void(0);" onClick='loadNewCaptchaImage()'>Reload</a>

Add jQuery JavaScript to load a new image for users:

loadNewCaptchaImage = function () {
      $("img.captchaimage").attr("src", "captcha.jpg" + "?" + (new Date()).getTime()); 
      $('input.captchaText').val('').focus();
}

You can always make the captcha more difficult by adding more font variations, text effects, and background noise. Here are some examples of what you can do to make it difficult for bots to crack the captcha: https://jcaptcha.atlassian.net/wiki/display/general/Samples+tests  Just don’t frustrate your users by making it near impossible for them to figure out what the CAPTCHA says.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s