JavaServer Pages jQuery CAPTCHA Code Example

The JSP jQuery Captcha code example shows how to integrate BotDetect CAPTCHA validation with jQuery Validation client-side form validation.

First Time Here?

Check the BotDetect JSP Captcha Quickstart for key integration steps.

It uses the Captcha Form Example as a starting point, and adds client-side jQuery Validation rules for all form fields.

Client-side validation is not secure by itself (it can be bypassed trivially), so the example also shows how the protected form action must always be secured by server-side CAPTCHA validation first, and use client-side validation only to improve the user experience.

Downloaded Location

The JSP jQuery CAPTCHA Example is included in examples folder of the download package as bdc3-jsp-jquery-validation-captcha-example.war file. Deploying (unpacking) the file will create a standard JSP directory tree.

index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="botDetect" uri="botDetect"%>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>BotDetect CAPTCHA jQuery validation Example</title>
    <link rel="stylesheet" href="stylesheet.css" type="text/css"/>
    <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
    <script type="text/javascript" src="js/jquery.validate.min.js"></script>
  </head>
  <body>
    <form id="form1" action="jqueryValidationAction" method="post">
      <h1>BotDetect CAPTCHA jQuery validation Example</h1>
      <fieldset>
        <legend>CAPTCHA Validation</legend>
        <label for="captchaCodeTextBox" class="prompt">
            Retype the code from the picture:</label>
        <!-- Adding BotDetect Captcha to the page-->
        <botDetect:captcha id="jqueryValidatedCaptcha" />
        <div class="validationDiv">
          <input id="captchaCodeTextBox" type="text" 
              name="captchaCodeTextBox" class="captchaVal"/>
          <br>
          <input type="submit" name="validate" value="Validate" />
          <label class="correct">${messages.captchaCodeCorrect}</label>
          <label class="incorrect">${messages.captchaCodeIncorrect}</label>
        </div>
      </fieldset>
      <script type="text/javascript" src="js/validation_rules.js"></script>
    </form>
  </body>
</html>

The input form code is similar to Captcha tag example index.jsp. Differences are that we must include jQuery, the jQuery.validate plugin and the script containing our custom validation rules. Also note that we use default userInputClientId ("captchaCodeTextBox") instead customized as in aforementioned example.

validation_rules.js

$(document).ready(function() {
  $("#form1").validate({
    // only validate fields when the form is submitted:
    // the Captcha input must only be validated when the whole code string is
    // typed in, not after each individual character (onkeyup must be false);
    // onfocusout validation could be left on in more complex forms, but 
    // doesn't fit this example
    onkeyup: false,
    onfocusout: false, 
    // customize client-side error display elements
    errorClass: "incorrect",
    validClass: "correct",
    errorElement: "label",
    // show the client-side error label to the left of the textbox
    errorPlacement: function(error, element) {
      $(".correct").text("");
      offset = element.offset();
      error.insertAfter(element);
    },
    // always reload the Captcha image if remote validation failed,
    // since it will not be usable any more (a failed validation attempt
    // removes the attempted code for necessary Captcha security
    showErrors: function(errorMap, errorList) {
      this.defaultShowErrors();
      for (var i=0; i<errorList.length; i++) {
        var element = errorList[i].element;
        var message = errorList[i].message;
        // check element css class and does the error message match remote
        // validation failure
        if (element.className.match(/captchaVal/) &&
            message === this.settings.messages[element.id].remote) {
              element.Captcha.ReloadImage();
        }
      }
    },
    submitHandler: function(form){
        form.submit();
    }
    
  });

  $(".captchaVal").rules('add', {
    required: true,
    remote: $(".captchaVal").get(0).Captcha.ValidationUrl,
    messages: {
      required: 'Required (client)',
      remote: 'Incorrect (client)'
    }
  });
  
});

To set up jQuery validation of a textbox taking Captcha code input, we do the following:

  • Add validation rules specifying it as a required field, that also needs to be validated remotely when a value is entered; define error messages shown when these validation rules are triggered
  • Disable onkeyup validation of the Captcha code, since we must validate the whole code and not the individual characters
  • Reload the Captcha image whenever remote Captcha validation fails

CAPTCHA jQuery Validation Rules and Error Messages

Since we're not using the input textbox Id for jQuery validation rules, we need to add them dynamically after the default validator has been initialized; that's why they're located after the $("#form1").validate({ call.

The expression for the remote Captcha validation Url can easily be read looking at dot-separated segments from right to left:

  • The ValidationUrl property is part of the BotDetect client-side API, and is unique for each Captcha instance
  • The client-side BotDetect object can always be accessed through the Captcha property of the Captcha code textbox.
  • We use the standard jQuery selector to access the textbox by Css class, and the .get(0) function call to get the underlying DOM element. This is needed because BotDetect adds the custom Captcha property to the DOM element directly, and the jQuery wrapper element returned by the jQuery selector doesn't include it.

Disabling Remote CAPTCHA Validation on Individual Character Input

Since BotDetect deletes the Captcha code stored on the server after failed validation of a particular Captcha instance, we must avoid validating the user input before the user finished typing in the whole Captcha code. The simplest way to achieve this is to disable onkeyup validation completely (onkeyup: false).

Reloading the CAPTCHA Image When Remote Validation Fails

Due to the above (failed validation invalidates the stored code for security purposes), we must also always reload the Captcha image when remote validation fails. Otherwise the user would be trying to correct his input based on an expired image, and couldn't pass Captcha validation at all.

We do this by customizing the jQuery.validate showErrors callback: beside the regular functionality (this.defaultShowErrors), we also check that there is Captcha validation error (element.className.match(/captchaVal/) and that the error message indicates a remote validation failure (message === this.settings.messages[element.id].remote). If that is case, we call the ReloadImage() function on the client-side Captcha object (accessed through the textbox element as explained above).

JQueryValidationAction.java

package botdetect.examples.jsp.jquery_validation;

import botdetect.web.Captcha;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JQueryValidationAction extends HttpServlet {
  @Override
  protected void doPost(HttpServletRequest request, 
      HttpServletResponse response)
      throws ServletException, IOException {

    Map<String, String> messages = new HashMap<String, String>();
    request.setAttribute("messages", messages);
    Captcha captcha = Captcha.load(request, "jqueryValidatedCaptcha");
    
    // validate the Captcha to check we're not dealing with a bot
    boolean isHuman = captcha.validate(request, 
        request.getParameter("captchaCodeTextBox"));
    if (isHuman) {
      // Captcha validation passed, perform protected action
      messages.put("captchaCodeCorrect", 
          "Successful form submission (server).");
    } else {
      // Captcha validation failed, show error message
      messages.put("captchaCodeIncorrect", "Incorrect! (server)");
    }
    
    RequestDispatcher dispatcher = 
        getServletContext().getRequestDispatcher("/index.jsp");
    dispatcher.forward(request, response);
  }
}

We left the server-side Captcha validation in place, ensuring that any bots or users with JavaScript disabled have their input checked. For users with JavaScript enabled, errors in Captcha code input will be shown on the client without full form being POSTed to the server.

To run this example, botdetect.jar must be in the classpath.

Please Note

BotDetect Java Captcha Library v4.0.Beta3 is an in-progress port of BotDetect 4 Captcha, and we need you to guide our efforts towards a polished product. Please let us know if you encounter any bugs, implementation issues, or a usage scenario you would like to discuss.