Roll your own Ajax-Based Captcha in Grails

Recently, I was asked to come up with a better solution to our captcha needs. We have been using ReCaptcha, which is great but difficult to read at times, and has caused frustrated customers and lost sales. I found a great solution at which displays a number of graphics and let's the user choose the right one to prove they are human. Here is a screenshot of my implementation:

To make this work within Grails, I had to make several tweaks. The following files are required:

Create a new controller called Captcha. This can really be named anything, but if you do rename it, it will have to be updated in the jquery.simpleCaptcha-0.2.js file or passed in as an option via the javascript.

package com.berry

import com.berry.BCrypt  
import grails.converters.JSON

class CaptchaController {

    def index = {

        // Generate the SALT to be used for encryption and place in session
        def captchaSalt = session.captchaSalt ?: BCrypt.gensalt()
        session.selectedCaptchaText = null
        session.captchaSalt = captchaSalt

        // Modify below for custom images
        def images = [
                'house':        'images/captchaImages/01.png',
                'key':          'images/captchaImages/04.png',
                'flag':         'images/captchaImages/06.png',
                'clock':        'images/captchaImages/15.png',
                'bug':          'images/captchaImages/16.png',
                'pen':          'images/captchaImages/19.png',
                'light bulb':   'images/captchaImages/21.png',
                'musical note': 'images/captchaImages/40.png',
                'heart':        'images/captchaImages/43.png',
                'world':        'images/captchaImages/99.png'

        // Create the image array to be returned in JSON format
        def size = images.size()
        def num = Math.min(params.numImages ?'numImages') : 5, size)
        def keys = images.keySet().toList()
        def used = []
        def random = new Random()
        (1..num).each { i ->
            def idx = random.nextInt(keys.size())
            def item = keys.get(idx)
            used << item

        // Select the 'chosen' text to be used for authentication and place in session
        def selectedText = used[random.nextInt(used.size())]
        def hashedSelectedText = BCrypt.hashpw(selectedText, captchaSalt);
        session.selectedCaptchaText = hashedSelectedText

//        println "SELECTED: ${hashedSelectedText}"
//        println "USED: ${used.inspect()}"

        // Generate object to be returned
        def ret = [
                text: selectedText,
                images: []
        used.each { u ->
            ret['images'] << [hash: BCrypt.hashpw(u, captchaSalt), file: images[u]]

        render ret as JSON

What this controller does is return a JSON object with the data needed to generate the captcha. The JSON appears like so:


Now we just need to implement this in our GSP file and controller. Suppose we have a page like shown above with a pickup code and the last 4 digits of the persons phone number. With adding our captcha div and required javascript, our GSP would look like this:

<!-- PLACE IN HEADER -->  
<script type="text/javascript" src="${resource(dir:'js',file:'jquery.simpleCaptcha-0.2.js')}"></script>  
<style type="text/css">  
    img.simpleCaptcha {
        margin: 2px !important;
        cursor: pointer;
    img.simpleCaptchaSelected {
        margin-bottom: 0px;
        border-bottom: 2px solid red;

<!-- BODY CONTENTS -->  
<g:form action="pickup">

    <div class="stylized myform" style="width:542px;">
        <h2>Your pickup code will be given to you by your loan consultant</h2>

        <g:if test="${flash.message}">
            <div class="error">

        <div class="clearfix formField">
            <label class="label_only">Pickup Code</label>
            <g:textField name="pickupCode" value="${pickupCode}" autocomplete="no" class="text" />
        <div class="clearfix formField">
            <label class="label_only">Last 4 Digits of Phone</label>
            <span class="after_checkbox" style="padding-right: 0px;">(XXX) XXX-</span>
            <g:textField name="lastFourDigits" value="${lastFourDigits}" autocomplete="no" class="text" maxLength="4" />
        <div class="clearfix formField">
            <label class="label_only">Are You Human?</label>
            <div style="float: left; margin-left: 10px;">
              <!-- Begin Captcha -->
                <div id="captcha"></div>
              <!-- End Captcha -->
        <div class="clearfix" style="margin-top: 15px;">
            <label class="label_only">&nbsp;</label>
            <g:submitButton name="submit" value="Show Me My Offer" class="button" />



<script type="text/javascript">  
    $(document).ready(function() {
            numImages: 5,
            introText: '<p>Are you human? Then pick out the <strong class="captchaText"></strong></p>'

Finally, we need to perform the validation on the controller side. The modified authentication action would look like the following:

def pickup = {

    // Determine if the captcha is picked correctly
    if (params.captchaSelection != session.selectedCaptchaText) {

      // They selected the correct Captcha image. Continue with Authentication

    } else {
        flash.message = "You did not click the correct image below. Please Try Again."   


So there ya go. It's actually pretty easy and customers seem to like choosing an image much more than typing a word that is difficult to read.