Sunday, February 27, 2011

Integrating reCaptcha in wicket

Intro

Recaptcha is a very nice widget used to stop spam on your blogs or prevent automated form completion.

This post is about how you can integrate the reCaptcha in your wicket application.

Usage

We will create a panel called RecaptchaPanel. In order to use this component to your application all you'll have to do is this:

add(new RecaptchaPanel("recaptcha"));
and of course, add the component in your markup:


Implementation

Implementation is simple. All you have to do, is to follow several steps:

Add recaptcha dependency to your project


 net.tanesha.recaptcha4j
 recaptcha4j
 0.0.7

This library hides the implementation details and expose an API for dealing with recaptcha service.

Create associated markup (RecaptchaPanel.html)



  

Create RecaptchaPanel.java

import net.tanesha.recaptcha.ReCaptcha;
import net.tanesha.recaptcha.ReCaptchaFactory;
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;

/**
 * Displays recaptcha widget. It is configured using a pair of public/private keys which can be registered at the
 * following location:
 * 
* https://www.google.com/recaptcha/admin/create
 * 
* More details about recaptcha API: http://code.google.com/apis/recaptcha/intro.html * * @author Alex Objelean */ @SuppressWarnings("serial") public class RecaptchaPanel extends Panel { private static final Logger LOG = LoggerFactory.getLogger(RecaptchaPanel.class); @SpringBean private ServiceProvider serviceProvider; public RecaptchaPanel(final String id) { super(id); final ReCaptcha recaptcha = ReCaptchaFactory.newReCaptcha(serviceProvider.getSettings().getRecaptchaPublicKey(), serviceProvider.getSettings().getRecaptchaPrivateKey(), false); add(new FormComponent("captcha") { @Override protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) { replaceComponentTagBody(markupStream, openTag, recaptcha.createRecaptchaHtml(null, null)); } @Override public void validate() { final WebRequest request = (WebRequest)RequestCycle.get().getRequest(); final String remoteAddr = request.getHttpServletRequest().getRemoteAddr(); final ReCaptchaImpl reCaptcha = new ReCaptchaImpl(); reCaptcha.setPrivateKey(serviceProvider.getSettings().getRecaptchaPrivateKey()); final String challenge = request.getParameter("recaptcha_challenge_field"); final String uresponse = request.getParameter("recaptcha_response_field"); final ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(remoteAddr, challenge, uresponse); if (!reCaptchaResponse.isValid()) { LOG.debug("wrong captcha"); error("Invalid captcha!"); } } }); } }

Things to notice:

  • ServiceProvider - is a spring bean containing reCaptcha configurations (public key and private key). These keys are different depending on the domain where your application is deployed (by default works for any key when using localhost domain). You can generate keys here: https://www.google.com/recaptcha/admin/create

  • The RecaptchaPanel contains a FormComponent, which allows implementing validate method, containing the validation logic.

  • Because reCaptcha use hardcoded values for hidden fields, this component cannot have multiple independent instances on the same page.

This component can be easily added to wicket-stuff or any other components library.

Conclusion

This post shows how easy you can integrate recaptcha widget in your application using wicket web framework.

9 comments:

  1. Thanks for this Alex - it was very useful.

    You missed one thing out that may confuse new users. You also need to create a file called "RecaptchaPanel.html" with the following contents:

    <wicket:panel>
    <div wicket:id="captcha" ></div>
    </wicket:panel>

    And place this in the resources directory, in the corresponding subdirectory to RecaptchaPanel.java.

    Also - could you please clarify the license on this code? Is it available to be used in commercial software?

    ReplyDelete
  2. Also, Line 27 should read:

    add(new FormComponent<Serializable>("captcha", new Model<Serializable>()) {

    Otherwise you will get a "No get method defined for class" error message as wicket will look for a getCaptcha() method in your model.

    ReplyDelete
  3. This was very useful but I am getting an error that ReCaptchaImpl cannot be serialized. Any ideas?

    ReplyDelete
    Replies
    1. Any field included in a Serializable objects needs to implement Serializable. Remove the field "final ReCaptcha recaptcha" and I think it will work.

      Delete
  4. I had some troubles trying to include the recaptcha in a nested form component, till I changed the extension of the panel for: RecaptchaPanel extends FormComponentPanel.

    My two cents

    ReplyDelete
  5. Hi,A website made by an expert web developer is not only unique but it also helps Web Design Cochin to achieve high ranking in the search engines. Technology has made web development a convenient process. Today web developers make websites for both developers and users.Thanks....

    ReplyDelete
  6. its really a helpful and effective post. I appreciate this kind of information. SEO services Tulsa

    ReplyDelete
  7. I was wondering if a different approach might make it easier to integrate into existing forms.

    What if, instead of creating a Panel, you simply created a FormComponent and then devs can simply add that to their existing forms and 'voila' - just works.

    ReplyDelete
  8. I really appreciate your article. the post has excellent tips which are useful. this post is good in regards of both knowledge as well as information.Bel Technology

    Web development company in Jaipur


    ReplyDelete