Its funny how my mind works – I was listening to NPR's Weekend Edition over the weekend and they were going through listener's comments. At the end of that segment, they mentioned that they were not going to accept email comments to their old address. They want listeners to go to npr.org and fill out a web form to enter their comments. The first thought I had was spam – It had to be spam. Having an open email address that's published must get a ton of spam. I know I get a ton of spam daily and so I'm guessing NPR must have been getting thousands and thousands of spam messages.
So as I'm driving along, I started thinking about moving to the web-form model to solicit feedback and I just assumed that they would take the next logical step and add Captcha to their web app. If you don't know, Captcha (completely automated public Turing test to tell computers and humans apart) is an acronym for a type of challenge-response test to determine whether or not the user is human. Thinking about Captcha got me thinking about JCaptcha, an open-source Java framework for Captcha definition and integration. I knew about JCaptcha as I had read about it on Dion's blog a while back and so I finally decided to download and give JCaptcha a try.
I was impressed with how easy it was to incorporate Captcha into an existing application. Here is a simple web-app I built using the 5 minutes application integration tutorial on the JCaptcha wiki.
Here's the JSP that acts as an entry into the application:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>Sample JCaptcha
A captcha (an acronym for "completely automated public Turing test to tell computers and humans apart") is a type of challenge-response test used in computing to determine whether or not the user is human.
To initialize the Captcha service, you create a singleton to instantiates an instance of the ImageCaptchaService that provides the facility to cache the Captcha and create the image.
package com.j2eegeek.jcaptcha.common;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;
/**
* The CaptchaServiceSingleton implements the Singleton patterns and returns an instance of the
* ImageCaptchaService.
*/
public class CaptchaServiceSingleton {
private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();
public static ImageCaptchaService getInstance() {
return instance;
}
}
Once we've created an instance of the ImageCaptchaService, we can create a servlet that will allow us to create an image. The servlet ends up calling the singleton to get an instance of the CaptchaService Singleton and calling its getChallenge() method.
/**
* The ImageCaptchaServlet class creates the actual image that's displayed to the user for validation.
* The servlet ends up calling the singelton to get an instance of the CaptchaService Singleton and calling its
* getChallenge method.
*/
public class ImageCaptchaServlet extends J2EEGeekBaseServlet {
private static final Log log = LogFactory.getLog(ImageCaptchaServlet.class);
public void doWork(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
byte[] captchaChallengeAsJpeg = null;
// the output stream to render the captcha image as jpeg into
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
// get the session id that will identify the generated captcha.
String captchaId = req.getSession().getId();
// call the ImageCaptchaService getChallenge method
BufferedImage challenge = CaptchaServiceSingleton.getInstance().getImageChallengeForID(captchaId, req.getLocale());
// a jpeg encoder
JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
} catch (IllegalArgumentException e) {
log.error("IllegalArgumentException exception - " + e.getCause().getMessage());
res.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} catch (CaptchaServiceException e) {
log.error("CaptchaServiceException exception - " + e.getCause().getMessage());
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
// flush it in the response
res.setHeader("Cache-Control", "no-store");
res.setHeader("Pragma", "no-cache");
res.setDateHeader("Expires", 0);
res.setContentType("image/jpeg");
ServletOutputStream out = res.getOutputStream();
out.write(captchaChallengeAsJpeg);
out.flush();
out.close();
}
}
Once you've created the image and displayed it via the index.jsp page, you need to validate the response entered by the user.
public class ValidateServlet extends J2EEGeekBaseServlet {
private static final Log log = LogFactory.getLog(ValidateServlet.class);
public void doWork(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("text/html");
ServletOutputStream out = res.getOutputStream();
out.println("");
Boolean isResponseCorrect = Boolean.FALSE;
String captchaId = req.getSession().getId();
String response = req.getParameter("j_captcha_response");
try {
isResponseCorrect = CaptchaServiceSingleton.getInstance().validateResponseForID(captchaId, response);
} catch (CaptchaServiceException e) {
log.error("Exception - " + e.getCause().getMessage());
}
if (isResponseCorrect.booleanValue()) {
out.println("
Success -- Try again?
");
} else {
out.println("
Failure -- Try again");
}
}
}
Here's all the code that's essentially a rip-off from the wiki as a IDEA project. Another great resource is the JavaWorld article that Dion points to written by Anand Raman. He goes into details about incorporating Captcha into JAAS.
No tag for this post.