Child pages
  • 5 minutes application integration tutorial
Skip to end of metadata
Go to start of metadata

This page demonstrates how to use jcaptcha in your application.

This tutorial uses the CaptchaService and sub components (see Architecture overview).

Install it

Maven2 users

Add the following dependency to your project POM

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

Without Maven2

Add jcaptcha-all.jar (provided in bin-distribution) and commons-collection-3.2 or greater (not provided see commons collection ) to your application class path, ie in you WEB-INF/lib folder.

Implement a CaptchaService

It must be a singleton

import com.octo.captcha.service.image.ImageCaptchaService;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;

public class CaptchaServiceSingleton {

    private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();

    public static ImageCaptchaService getInstance(){
        return instance;
    }
}

Code an image captcha servlet

import com.octo.captcha.service.CaptchaServiceException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;


public class ImageCaptchaServlet extends HttpServlet {


    public void init(ServletConfig servletConfig) throws ServletException {

        super.init(servletConfig);

    }


    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 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.
        //the same id must be used to validate the response, the session id is a good candidate!
        String captchaId = httpServletRequest.getSession().getId();
        // call the ImageCaptchaService getChallenge method
            BufferedImage challenge =
                    CaptchaServiceSingleton.getInstance().getImageChallengeForID(captchaId,
                            httpServletRequest.getLocale());

            // a jpeg encoder
            JPEGImageEncoder jpegEncoder =
                    JPEGCodec.createJPEGEncoder(jpegOutputStream);
            jpegEncoder.encode(challenge);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        } catch (CaptchaServiceException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }

        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();

        // flush it in the response
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream =
                httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaChallengeAsJpeg);
        responseOutputStream.flush();
        responseOutputStream.close();
    }
}

Add it to your web.xml

<servlet>
        <servlet-name>jcaptcha</servlet-name>
        <servlet-class>ImageCaptchaServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>

And

<servlet-mapping>
        <servlet-name>jcaptcha</servlet-name>
        <url-pattern>/jcaptcha</url-pattern>
    </servlet-mapping>

The form

Provide a form containing the captcha challenge (the image) and an input text for the response

<img src="/jcaptcha">
<input type='text' name='j_captcha_response' value=''>

the validation routine

Handle the post (using a servlet, or whatever).
The validateCaptchaForId method will return true if the response is correct.

Boolean isResponseCorrect =Boolean.FALSE;
           //remenber that we need an id to validate!
           String captchaId = httpServletRequest.getSession().getId();
           //retrieve the response
           String response = httpServletRequest.getParameter("j_captcha_response");
           // Call the Service method
            try {
                isResponseCorrect = CaptchaServiceSingleton.getInstance().validateResponseForID(captchaId,
                        response);
            } catch (CaptchaServiceException e) {
                 //should not happen, may be thrown if the id is not valid
            }

//do something according to the result!

Want some more

Try customization and more integration

Have a look to the modules provided to see how to integrate it with other frameworks :

Contribute !

Please put some code showing integration with a new framework in a child page of the How To

  • No labels

26 Comments

  1. Anonymous

    hehe, you might want to add a captcha here! (smile)

  2. Anonymous

    Thank you, great way to start! / Frans Lundberg

  3. Calling

    CaptchaServiceSingleton.getInstance().validateResponseForID(captchaId,
    captcha_response);

    twice gives me

    com.octo.captcha.service.CaptchaServiceException: Invalid ID, could not validate

    I am trying to check the response via an ajax service, so may call this a few times.

    First time, after the captcha is created, it is fine.
    Called again it gives me the above exception.

    Is this supposed to happen, that you can only validate once?

    Is there a good way to find out what the word should be, so that I don't have to run validate?

  4. Hi,

    I'm trying to create a JSP page to display "the form" section listed above. I have several questions:

    1: Could you write the code into the tutorial for how to instantiate the CaptchaService on that page? Do I need to create a reference to a bean on that page? If so, please show me how exactly to write all of the code for that page to work.

    2: how does writing "jcaptcha" for the "img src" in <img src="jcaptcha"> create an image? I feel like there should be some reference to the CaptchaService to be able to correctly display an image from the service? Should something else be written there for me to get it to work?

    3: when you say "Handle the post (using a servlet, or whatever)" does that mean we can just post the form to another JSP page with the validation routine? I'm not clear what you are referring to.

    Thank you for your help!

  5. Hooha!!(smile)

    It works for me(thumbs up)

    Very well guided
    Thanks !!

  6. The implementation described above worked fine for me but I want to use a different type of image (one that is easier to understand the letters). I implemented the CaptchaServiceSingleton as follows:

    private static ImageCaptchaService instance = initializeService();

    private static ImageCaptchaService initializeService()
    {
    SimpleListImageCaptchaEngine engine = new SimpleListImageCaptchaEngine();
    engine.addFactory(new GimpyFactory(new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), new ComposedWordToImage(new RandomFontGenerator(new Integer(12), new Integer(16)), new FunkyBackgroundGenerator(new Integer(200), new Integer(100)), new RandomTextPaster(new Integer(6), new Integer(10), Color.BLACK))));
    return new DefaultManageableImageCaptchaService(new FastHashMapCaptchaStore(), engine, 180, 100000, 75000);
    }

    I then create the image as described except using only the user's session id. The image is created as expected but when I call validateResponseForID, no matter what the response is (even an empty string), it always returns true. Did I do something wrong? What could be causing it to work improperly?

    1. Hello Nathan. I have done the same thing and am experiencing the same problems. Did you ever find a solution to this?

  7. I implemented a modified version of the CaptchaServiceSingleton to change the default image size and background color:

    public static ImageCaptchaService getInstance()
    {
        ListImageCaptchaEngine engine = new SimpleListImageCaptchaEngine();
        /* I instantiate here a FontGenerator, BackgroundGenerator, TextPaster and WordGenerator */
        ComposedWordToImage composedWordToImage = new ComposedWordToImage(fontGenerator, backgroundGenerator, textPaster);
        engine.addFactory(new GimpyFactory(wordGenerator, composedWordToImage));
        return new DefaultManageableImageCaptchaService(new FastHashMapCaptchaStore(), engine, MIN_STORAGE_DELAY, MAX_STORE_SIZE, LOAD_BEFORE_GARBAGE_COLLECTION);
    }

    My problem is that when I load the page, I sometimes get the desired image size and background and sometimes not (in this case, I get the funky 3 color gradient background)... I guess it is because the engine is initialized with a default captcha factory? For this reason, I tried to do the following to reset the engine factories:

    ImageCaptchaFactory[] captchaFactories =

    Unknown macro: {new GimpyFactory(wordGenerator, composedWordToImage)}

    ;
    engine.setFactories(captchaFactories);

    After that, nothing works anymmore... Any idea on what I should do?

    1. Greetings, Jimmy.

      SimpleListImageCaptchaEngine class is already has own factories. If you want do define custom CaptchaEngine I suggest to create your engine implementation and override buildInitialFactories() method in it.

      For example:

      public class MyImageCaptchaEngine extends ListImageCaptchaEngine {
           protected void buildInitialFactories() {
                  WordGenerator wgen = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789");
                  RandomRangeColorGenerator cgen = new RandomRangeColorGenerator(
                       new int[] {0, 100},
                       new int[] {0, 100},
                       new int[] {0, 100});
                  TextPaster textPaster = new RandomTextPaster(new Integer(7), new Integer(7), cgen, true);
      
                  BackgroundGenerator backgroundGenerator = new FunkyBackgroundGenerator(new Integer(200), new Integer(100));
      
                  Font[] fontsList = new Font[] {
                      new Font("Arial", 0, 10),
                      new Font("Tahoma", 0, 10),
                      new Font("Verdana", 0, 10),
                   };
      
                   FontGenerator fontGenerator = new RandomFontGenerator(new Integer(20), new Integer(35), fontsList);
      
                   WordToImage wordToImage = new ComposedWordToImage(fontGenerator, backgroundGenerator, textPaster);
                   this.addFactory(new GimpyFactory(wgen, wordToImage));
           }
      }
      

      If so, your CaptchaServiceSingleton should be like the following:

      public class CaptchaServiceSingleton {
          private static ImageCaptchaService instance;
          static {
          	instance = new DefaultManageableImageCaptchaService(
          		new FastHashMapCaptchaStore(),
          		new MyImageCaptchaEngine(),
          		180,
          		100000,
          		75000);
          }
          public static ImageCaptchaService getInstance(){
              return instance;
          }
      }
      

      Regards, Dmitry Kudrenko (ARDAS group)

      1. This was a great post; thank you! I was able to get it working in my application perfectly. I adjusted the image size and the number of characters to meet my specific requirements. I just have one question: the text is being pasted in on the background, right on the left edge. Is there a way to center the text, or at least to have a left margin?

  8. I can confirm the bug reported by Jimmy.

    Here is the exception:
    java.lang.ExceptionInInitializerError
    at net.bacman.utils.ImageCaptchaServlet.doGet(ImageCaptchaServlet.java:42)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:115)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:92)
    at com.caucho.server.dispatch.ServletFilterChain.doFilter(ServletFilterChain.java:106)
    at com.caucho.server.cache.CacheFilterChain.doFilter(CacheFilterChain.java:209)
    at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:173)
    at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:229)
    at com.caucho.server.hmux.HmuxRequest.handleRequest(HmuxRequest.java:420)
    at com.caucho.server.port.TcpConnection.run(TcpConnection.java:511)
    at com.caucho.util.ThreadPool.runTasks(ThreadPool.java:520)
    at com.caucho.util.ThreadPool.run(ThreadPool.java:442)
    at java.lang.Thread.run(Thread.java:619)
    Caused by: com.octo.captcha.engine.CaptchaEngineException: This factory is not an image captcha factory class com.octo.captcha.image.gimpy.GimpyFactory
    at com.octo.captcha.engine.image.ListImageCaptchaEngine.setFactories(ListImageCaptchaEngine.java:95)
    at net.bacman.utils.CaptchaServiceSingleton.<clinit>(CaptchaServiceSingleton.java:39)
    ... 12 more

    The setFactories method:

    /**
         * @param factories new captcha factories for this engine
         */
        public void setFactories(CaptchaFactory[] factories) throws CaptchaEngineException {
            if (factories == null || factories.length == 0) {
                throw new CaptchaEngineException("impossible to set null or empty factories");
            }
            ArrayList tempFactories = new ArrayList();
    
            for (int i = 0; i < factories.length; i++) {
                if (ImageCaptchaFactory.class.isAssignableFrom(factories[i].getClass())) {
                    throw new CaptchaEngineException("This factory is not an image captcha factory " + factories[i].getClass());
                }
                tempFactories.add(factories[i]);
            }
    
            this.factories = tempFactories;
        }
    

    I thought perhaps I should use the CustomFactoriesImageCaptchaEngine.java, but that class isn't in the build.   Any thoughts on how this should be fixed?

    Thanks,
    Michael 

  9. I have implemented 5 minutes application integration using jcaptcha-all-1.0-RC6.jar. It works but sometimes, I am getting weird images (which are not letters or numbers or not even a type able symbol from keyboard). httpServletRequest.getLocale() also returns en_US. Can anybody help me how to get rid of these weird images? The image is seen as follows

    I also failed to implement the customize images using this example. How to get the customize images or background other than default one. Thanks

  10. Hi Marc

    Great article.

    You stated that the CaptchaService must be instantiated as a singleton.

    Will this have any issue in a clustered environment?

    Regards

  11. The codec used for JPEG generation will be deprecated in futere releases of JDK. It should be changed with the following lines:

    import javax.imageio.ImageIO;
    ........

    public class ImageCaptchaServlet extends HttpServlet {

       protected void doPost(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {

            response.setHeader("Cache-Control", "no-store");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
           
            response.setContentType("image/png");       
           
            //the same id must be used to validate the response,
            //the session id is a good candidate!
            String captchaId = request.getSession().getId();
            // call the ImageCaptchaService getChallenge method
            BufferedImage image =
                    CaptchaServiceSingleton.getInstance().getImageChallengeForID(captchaId,
                    request.getLocale());       
           
           
            ServletOutputStream os = response.getOutputStream();
            //Writing the image to outputstream
            ImageIO.write(image, "png", os);
            // flush it in the response
            os.flush();
            os.close();
       }
    ......
    }

  12. rm

    Please help i need documentation or a tutorial for integrating the sound captcha functionallity i cannot find anything, it would be nice to have a tutorial like the 5 min app integration using sound captcha.

    thanks.

    1. Fra

      Hi,
      I am looking for JCaptcha with sound integration.

      It seems JCaptcha is supporting sound. But I don't know how to integrate sound with JCaptcha.
      Anyone please send the steps to integrate JCaptcha with sound.

      Adv. thanks,

  13. Hi Eric,

    I thought about the same. But if you configure your load balancer to have sticky sessions, then it's no problem, because the second request containing the response on the challenge will land in the same application server, hence in the same JVM. The only problem with this is in the rare case when the first request is sent to server A, then it crashes, then the second request goes to server B - it will fail then, of course. But then the user repeats the submit with a different challenge coming from server B, and it will (hopefully) succeed.

  14. JCaptcha Integration with Tapestry

    This is for Apache Tapestry users, we can integrate JCaptcha in Tapestry application easily as it supports StreamResponses, Therefore don't need any Servlet to be maintained.

    First create a tapestry service for JCaptcha

    public interface MyCaptchaService {
        public boolean isValidUserResponse(String verificationCode, String captchaId);
        public StreamResponse generateImageChallenge(String captchaId);
    }
    

    Implementation class

    public class MyCaptchaServiceImpl implements MyCaptchaService {
       
        private final ImageCaptchaService serviceInstance;  
       
        public MyCaptchaServiceImpl(ImageCaptchaService serviceInstance {
            this.serviceInstance = serviceInstance;         
        }
       
        public StreamResponse generateImageChallenge(String captchaId) {
           
            BufferedImage imageChallenge = serviceInstance.getImageChallengeForID(captchaId, Locale.US);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
         
            try {           
                ImageIO.write(imageChallenge, "PNG", os);           
                final InputStream is = new ByteArrayInputStream(os.toByteArray());
                return new StreamResponse() {
                    public String getContentType()  { return "image/png"; }
                    public   InputStream getStream() throws IOException { return is; }
                    public void prepareResponse(Response response) {
                        response.setHeader("Pragma", "no-cache");
                        response.setHeader("Cache-Control", "no-cache");
                        response.setDateHeader("Expires",0);
                    }
                };
            }
            catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        public boolean isValidUserResponse(String verificationCode, String captchaId) {
           
            if (Boolean.parseBoolean(properties.value("captcha.validate.skip")))
                return true;
         
            try {
                return serviceInstance.validateResponseForID(captchaId, verificationCode);
            } catch (CaptchaServiceException e) {
                throw new RuntimeException(e);
            }
        }
       
        public class ImageCaptchaImpl extends ImageCaptcha{
            public static final long serialVersionUID = 1;
            public ImageCaptchaImpl(String question, BufferedImage challenge) {
                super(question,challenge);
               
            }
            public Boolean validateResponse(Object obj){
                return Boolean.FALSE;
            }
           
        }
    }
    

     Add a build method for this service in your AppModule.java

    public MyCaptchaService buildCaptchaService() {
            return new MyCaptchaServiceImpl(new DefaultManageableImageCaptchaService());
        }
    

     The page where you use ex: MyPage.tml

    <img xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" src="${imageURL}"  align="absmiddle" alt="Dynamic Verification Code" /> 
    

     in MyPage.java

    public class MyPage {
    
        @Inject private ComponentResources resources;
        @Inject private CaptchaService captchaService;
        @Inject private RequestGlobals requestGlobals;
             
    
        public Link getImageURL() {
            return resources.createActionLink("image", false);
        }
        //executes when image is rendered
        public Object onImage() {    
            return captchaService.generateImageChallenge(requestGlobals.getHTTPServletRequest().getSession().getId());
        }
    }
     
    

              when you want to verify

              

    	   @Inject             private MyCaptchaService      captchaService;
               @Inject             private RequestGlobals      requestGlobals; 
                String sessionId = requestGlobals.getHTTPServletRequest().getSession().getId();
                if (!captchaService.isValidUserResponse(verificationCode, sessionId)) {
                    form.recordError("Verification code you entered does not match");
                }
    

     I would like to thanks Mr. Dmitry Kudrenko who has posted about extending of Captcha engine.

    Best Regards
    Lakshitha Ranasinghe,
    Colombo, Sri Lanka. 

  15. Hi Lakshitha Ranasinghe,

    Can you write addition a tutorial for JCaptcha Integration with Tapestry 4 ? At present, between Tapestry 4 and Tapestry 5 have very large difference. For instance, Tapestry 4 doesn't support the StreamResponse interface and other cleasses/service.

    please help me to take an artical about JCaptcha Integration with Tapestry 4.

    Thank you very much,

    Bao

  16. It worked when I used (JSP + servlet) but will it also work if I implement it on (HTML + VelocityServlet) ?

  17. Hi guys,

    I have integrated jcaptcha in my projcet it is working fine.But code in the servlet using some of the sun proprietary api classes so i am getting this kind of warning messages.Can any one tell me how to avoid these warnings.

    These are the two lines using the proprietary api

    JPEGImageEncoder jpegEncoder =
    JPEGCodec.createJPEGEncoder(jpegOutputStream);
    jpegEncoder.encode(challenge);

    warning: com.sun.image.codec.jpeg.JPEGCodec is Sun proprietary API and may be removed in a future release

    warning: com.sun.image.codec.jpeg.JPEGImageEncoder is Sun proprietary API and may be removed in a future release

    Thankyou,
    ravindra

  18. Hi all,

    I am able to generate captcha without any warning messages.But the captcha is generating words containing only alphabets.I want to generate alpha nuemeric captcha.Can anyone tell me how to generate
    alpha numeric captcha.

    Thankyou,
    ravindra

  19. a colleague and i have written an article about GWT, which includes a description of how to integrate JCaptcha with GWT. The article and the free source code are at:

    http://www.maxant.co.uk/whitepapers.jsp

    The paper is called "Enterprise GWT: Combining Google Web Toolkit, Spring and Other Features to Build Enterprise Applications"

  20. It's great!! Good job!! (wink)
    I am a Spanish novice programmer, and I would know how I can put a message below the form, as right or wrong answer.
    How could I?

  21. Hi All,

    Please can anyone help me to integrate the sound captcha and image captcha in the struts application.The requirment is letters which are generated in the image captcha could be listened by sound captcha and also validate the response submited by user.

    Thanks in advance.

    Thanks, Biswajit