Enterprise Java

Adding CAPTCHA to your GWT application

What is CAPTCHA?
In a world full of malicious bots, what can you do to protect your precious web application? One of the basic things that you really should do is add CAPTCHA capabilities to it. If you are not familiar with the (rather bizarre sounding) term, CAPTCHA is a simplistic way to ensure that a user is actually a real person, and not a computer. This can be done by challenging the user and asking from him to provide a response to a “problem”. Because computers are unable to solve the CAPTCHA, any user entering a correct solution is presumed to be human. The most common way is to ask the user to type letters or digits from a distorted image that appears on the screen.

You most likely have seen one of the CAPTCHA files when signing up to a site. The following is a CAPTCHA example from Wikipedia.


Add SimpleCaptcha into your application
In this tutorial I will incorporate CAPTCHA functionality to a web application. I am going to use the SimpeCaptcha framework, which is used in generating CAPTCHA image/answer pairs for Java. An installation guide is provided by the site, but this one refers to the plain old JSP based applications. I am going to show you how to integrate the framework with your fancy GWT project.

(I am going to assume that you already have GWT up and running on your system, along with the Google Plugin for Eclipse)

First, let’s create our Eclipse project. Select “File ? Web Application Project” and provide the necessary information as shown in the following image. The project’s name will be “CaptchaGwtProject”. Make sure that support for GWT is included, but Google App Engine is not.

Before we proceed, an important notice here. SimpleCaptcha makes heavy use of the AWT classes in order to perform the image rendering. However, as you might know, App Engine does not support all JRE classes and most specifically only a few from the AWT package are included. You can check the AppEngine JRE Class White List for more details. Thus, it is impossible to incorporate the framework in an application that will be deployed on App Engine and it should be used only with platforms running on standard JREs.

Next step is to download the library from SourceForge (I used the 1.1.1 version). Add the downloaded JAR file to your project’s classpath. Also, don’t forget to copy the JAR file to “CaptchaGwtProject\war\WEB-INF\lib” folder, since that will be needed by the embedded containter’s (Jetty) runtime. The project’s JavaDoc pages can be found here.

Setting up the client side
Eclipse will automatically create the application’s skeleton, also creating some sample files. Locate the CaptchaGwtProject class which is the application’s entrypoint. Remove the existing contents and replace them with the following:

package com.javacodegeeks.captcha.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class CaptchaGwtProject implements EntryPoint {

    private final SignupServiceAsync signupService = GWT.create(SignupService.class);
    
    private final Button sendButton = new Button("Sign Up");
    
    public void onModuleLoad() {
        
        final TextBox usernameField = new TextBox();
        usernameField.setText("Username here");
        
        final TextBox passwordField = new TextBox();
        passwordField.setText("Password here");

        final TextBox captchaField = new TextBox();
        captchaField.setText("CAPTCHA Word here");
        
        final Label responseLabel = new Label();        
        final Image captchaImage = new Image("/SimpleCaptcha.jpg");

        usernameField.setFocus(true);
        sendButton.addStyleName("sendButton");
        
        RootPanel.get("usernameFieldContainer").add(usernameField);
        RootPanel.get("passwordFieldContainer").add(passwordField);
        RootPanel.get("captchaFieldContainer").add(captchaField);
        RootPanel.get("sendButtonContainer").add(sendButton);
        RootPanel.get("captchaImageContainer").add(captchaImage);
        RootPanel.get("responseLabelContainer").add(responseLabel);

        class MyHandler implements ClickHandler, KeyUpHandler {
            public void onClick(ClickEvent event) {
                sendDataToServer();
            }
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    sendDataToServer();
                }
            }
            private void sendDataToServer() {                
                String username = usernameField.getText();
                String password = passwordField.getText();
                String captcha = captchaField.getText();
                sendButton.setEnabled(false);    
                signupService.performSignup(username, password, captcha, signupCallback);                
            }
        }

        MyHandler handler = new MyHandler();
        sendButton.addClickHandler(handler);
        usernameField.addKeyUpHandler(handler);
        
    }

    private AsyncCallback signupCallback = new AsyncCallback() {        
        @Override
        public void onSuccess(Boolean result) {
            if (result) {
                Window.alert("CAPTCHA was valid");
            }
            else {
                Window.alert("CAPTCHA was invalid");
            }            
            sendButton.setEnabled(true);
        }        
        @Override
        public void onFailure(Throwable caught) {
            Window.alert("Error occurred while communicating with server");
            sendButton.setEnabled(true);
        }        
    };
    
}

The code is straightforward and is based on the example class that is created automatically. We add two TextBoxes for the user input and a Label for the server response. We also add an Image instance, which will be a placeholder for our CAPTCHA. We set its URL to “/SimpleCaptcha.jpg” which will be handled by the framework. Finally, a Button is used in order to invoke the call to the server. The AsyncCallback is using a boolean to indicate the success of failure of the CAPTCHA validation on the server. Note that some essential parts of a web application (such as user input validation) are deliberately missing so that we can concentrate on the CAPTCHA part.

Next, locate the HTML file named “CaptchaGwtProject.html” inside the project’s “war” file. Edit the file and add some containers for our GWT objects. The code is the following:

<!doctype html>
<!-- The DOCTYPE declaration above will set the    -->
<!-- browser's rendering engine into               -->
<!-- "Standards Mode". Replacing this declaration  -->
<!-- with a "Quirks Mode" doctype may lead to some -->
<!-- differences in layout.                        -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!--                                                               -->
    <!-- Consider inlining CSS to reduce the number of requested files -->
    <!--                                                               -->
    <link type="text/css" rel="stylesheet" href="CaptchaGwtProject.css">

    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Web Application Starter Project</title>
    
    <!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" src="captchagwtproject/captchagwtproject.nocache.js"></script>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>

    <!-- OPTIONAL: include this if you want history support -->
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
    
    <!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
    <noscript>
      <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; 
      color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
        Your web browser must have JavaScript enabled
        in order for this application to display correctly.
      </div>
    </noscript>

    <h1>CAPTCHA Secured Web Application</h1>

    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your username:</td>
        <td id="usernameFieldContainer"></td>
      </tr>
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your password:</td>
        <td id="passwordFieldContainer"></td>
      </tr>
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter the word:</td>
        <td id="captchaFieldContainer"></td>
      </tr>
      <tr>
        <td id="sendButtonContainer"></td> 
      </tr>
      <tr>
        <td id="captchaImageContainer"></td> 
      </tr>
      <tr>
        <td colspan="2" style="color:red;" id="responseLabelContainer"></td>
      </tr>
    </table>
            
  </body>
</html>

Note that the only changes from the auto-generated file are after the <h1> tags.

Our asynchronous GWT service is going to be very simple and execute only one function. The two corresponding interfaces are shown below:

package com.javacodegeeks.captcha.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("signup")
public interface SignupService extends RemoteService {
 
    boolean performSignup(String username, String password, String userCaptcha);
 
}
package com.javacodegeeks.captcha.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface SignupServiceAsync {
 
    void performSignup(String username, String password, String userCaptcha,
        AsyncCallback callback);
 
}

(Note that the auto-generated “greetingService” classes have been removed)

Preparing the server side
On the server side, the main object that we use from the library is Captcha. To retrieve the Captcha’s value (and compare it to the user’s input) we have to obtain reference to the HttpSession object associated with the specific session. The HttpSession can be retrieved by the corresponding HttpServletRequest object. This is standard Java EE stuff. Do not forget that the server side GWT services inherit from the RemoteServiceServlet, which inherits from HttpServletRequest. The underlying request can be obtained by calling the getThreadLocalRequest method. Note that, as the API mentions, this is stored thread-locally so that simultaneous invocations can have different request objects.

The server-side concrete implementation is the following:

package com.javacodegeeks.captcha.server;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import nl.captcha.Captcha;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.javacodegeeks.captcha.client.SignupService;

@SuppressWarnings("serial")
public class SignupServiceImpl extends RemoteServiceServlet implements SignupService {

    public boolean performSignup(String username, String password, String userCaptcha) {
        
        HttpServletRequest request = getThreadLocalRequest();
        
        HttpSession session = request.getSession();

        Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME);
        
        return captcha.isCorrect(userCaptcha);
        
    }
}

Extending SimpleCaptcha
The final step is to setup the Servlet that will generate the image shown to the user. SimpleCaptcha can be easily extended by creating a class that inherits from the provided SimpeCaptchaServlet class. The relevant code is the following:

package com.javacodegeeks.captcha.server.servlet;

import static nl.captcha.Captcha.NAME;

import java.io.IOException;

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

import nl.captcha.Captcha;
import nl.captcha.backgrounds.GradiatedBackgroundProducer;
import nl.captcha.servlet.CaptchaServletUtil;
import nl.captcha.servlet.SimpleCaptchaServlet;

public class ExtendedCaptchaServlet extends SimpleCaptchaServlet {
    
    private static final long serialVersionUID = 6560171562324177699L;
     
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        HttpSession session = req.getSession();
        
        Captcha captcha = new Captcha.Builder(_width, _height)
        .addText()
        .addBackground(new GradiatedBackgroundProducer())
        .gimp()
        .addNoise()
        .addBorder()
        .build();

        session.setAttribute(NAME, captcha);
        CaptchaServletUtil.writeImage(resp, captcha.getImage());
        
    }
    
}

The “_width” and “_height” variables as passed as initialization parameters and read from the parent class. To create a new Captcha object we use the Captcha.Builder class (which relies on the builder pattern). We then pass the object to the specific session and stream the associate BufferedImage to the servlet response.

Note that our implementation generates a new image each time the user performs a page request. This is different from the default SimpleCaptcha implementation that uses the same image for a given session.

Configuring the web application
All the components are tied up via the web application’s “web.xml” descriptor, which in our case is the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>CaptchaGwtProject.html</welcome-file>
  </welcome-file-list>
  
  <!-- Servlets -->
  <servlet>
    <servlet-name>signupServlet</servlet-name>
    <servlet-class>
 com.javacodegeeks.captcha.server.SignupServiceImpl
    </servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>signupServlet</servlet-name>
    <url-pattern>/captchagwtproject/signup</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>SimpleCaptcha</servlet-name>
    <servlet-class>
 com.javacodegeeks.captcha.server.servlet.ExtendedCaptchaServlet
    </servlet-class>
    <init-param>
        <param-name>width</param-name>
        <param-value>200</param-value>
    </init-param>
    <init-param>
        <param-name>height</param-name>
        <param-value>50</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>SimpleCaptcha</servlet-name>
    <url-pattern>/SimpleCaptcha.jpg</url-pattern>
  </servlet-mapping>

</web-app>

We declare the GWT service (class “SignupServiceImpl”) and the welcome file. Nothing special here. Finally we declare the servlet that will take care of the image generation and handles the request under the “’/SimpleCaptcha.jpg” URL (remember this is used in the GWT entrypoint). We also provide the initialization parameters for our servlet (width and height).

That’s it! Run the project and you should see something like the following:

Each time you refresh the page, a new CAPTCHA image is created. When the “Sign Up” button is clicked, the field contents are sent to the server, where the user provide CAPTCHA value is tested against the one existing in the current session. In case of failure, we see this:


That’s it. Now your application can be a little bit more secure.

You can find the full Eclipse project here.

UPDATE: One of our readers asked whether it is possible to add a “reload image” functionality. It is indeed possible, however one has to be careful when refetching the image from the server. As stated at the following discussion, the browser will not refetch the image (since it has the same URL). The trick is to add a dummy parameter which will change with every refresh. Have a look at the article here:

http://groups.google.gy/group/google-web-toolkit/browse_thread/thread/be9f1da56b5b1c18

The thing is, I have created a new version of the project, which can be fetched (pun intended) here.

Enjoy!

Related Articles :

Ilias Tsagklis

Ilias is a software developer turned online entrepreneur. He is co-founder and Executive Editor at Java Code Geeks.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Pawan Shetty
Pawan Shetty
10 years ago

Hello thanks for the brilliant article. I have tried doing the same.. but it works locally not when deployed to appspot

Zmicer Szybieka
Zmicer Szybieka
10 years ago

First, thanks for nice example!
Secondly, The “_width” and “_height” variables from SimpleCaptchaServlet class are declared private. So they are invisible in ExtendedCaptchaServlet. Also next line “private AsyncCallback signupCallback = new AsyncCallback() { ” in CaptchaGwtProject.java wouldn’t be compiled, because compiler needs generic type “Boolean” to be specified explicitly.

Back to top button