del.icio.us Digg DZone Reddit StumbleUpon
How to reCAPTCHA Your Java Application - Willie Wheeler
« Previous | 1 | 2

Step 4: Validate the form, including the reCAPTCHA

You'll find it convenient to download the recaptcha4j library. It provides a simple API for submitting user responses to the reCAPTCHA server and finding out whether a user's response is valid.

At this point I'm just going to lay some code on you. As mentioned above I'm using Spring 2.5 MVC with annotations and Commons Validator, but the main thing is for you to look at how I'm using the ReCaptchaImpl class and just copy that.

import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

...

@RequestMapping(value = "/comments/postcomment.do", method = RequestMethod.POST)
public String doPost(
        HttpServletRequest req,
        @RequestParam("articleId") long articleId,
        @RequestParam("recaptcha_challenge_field") String challenge,
        @RequestParam("recaptcha_response_field") String response,
        @ModelAttribute("comment") Comment comment,
        BindingResult result) {
    
    // Validate the form (other than the reCAPTCHA)
    validator.validate(comment, result);
    
    // Validate the reCAPTCHA
    String remoteAddr = req.getRemoteAddr();
    ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
    
    // Probably don't want to hardcode your private key here but
    // just to get it working is OK...
    reCaptcha.setPrivateKey("<your_private_key>");
    
    ReCaptchaResponse reCaptchaResponse =
        reCaptcha.checkAnswer(remoteAddr, challenge, response);
    
    if (!reCaptchaResponse.isValid()) {
        FieldError fieldError = new FieldError(
            "comment",
            "captcha",
            response,
            false,
            new String[] { "errors.badCaptcha" },
            null,
            "Please try again.");
        result.addError(fieldError);
    }
    
    // If there are errors, then validation fails.
    if (result.hasErrors()) {
        String path = comment.getPagePath();
        log.debug("Form validation error; forwarding to " + path);
        return "forward:" + path;
    }
    
    // Else validation succeeds.
    log.debug("Form validation passed");
    comment.setIpAddress(remoteAddr);
    comment.setDate(new Date());
    
    // Post the comment
    log.debug("Posting the comment");
    articleService.postComment(articleId, comment);
    log.debug("Comment posted");
    
    return "redirect:" + comment.getPagePath() + "#comments";
}

Here's the API for FieldError since I know that's not clear from the code. Basically I'm using that to indicate that a validation error occurred and set up an error message for the user. If you're not using Spring/Validator then you'll do something else here.

The Comment class is just a class from my app, so don't worry about that one.

You did it

Good job. If you're feeling ambitious, try to defeat reCAPTCHA with super-advanced OCR. If you succeed then it represents an advance in OCR technology. Tell somebody and become famous. :-)

Social bookmarks: del.icio.us Digg DZone Reddit StumbleUpon
« Previous | 1 | 2

Comments (39)

Hi,
I was doing fine until I got to step 4.

I have no idea what to do with the codes in that step, could you please help me.

Thank you
By René Leclerc on Apr 12, 2008 at 5:26 PM PDT
Hi Rene. The step 4 code is an example of how to integrate reCAPTCHA with Spring MVC + Commons Validator. If you are not using those, then you will need to adapt the code accordingly. The main part that is relevant no matter what is this:

// Validate the reCAPTCHA
String remoteAddr = req.getRemoteAddr();
ReCaptchaImpl reCaptcha = new ReCaptchaImpl();

// Probably don't want to hardcode your private key here but
// just to get it working is OK...
reCaptcha.setPrivateKey("<your_private_key>");

ReCaptchaResponse reCaptchaResponse =
reCaptcha.checkAnswer(remoteAddr, challenge, response);

Hope that helps.
By Willie Wheeler on Apr 12, 2008 at 9:01 PM PDT
I also am confused on step 4 like Rene?? Where in my code does step 4 go?

Roy
By Roy Bingham on May 1, 2008 at 8:48 AM PDT
Sorry about the confusion. The code in question goes wherever you are processing the reCAPTCHA form data. So if you are using a servlet, for example, it would go there. If you're using a Struts action, it would go there. Or if you're using Spring MVC (as I'm doing in the example), it would go there. See my comment above (the one dated 04/12/08) for the essential part of the code. I'm pulling the remote address out of the HttpServletRequest and also using some request parameters. You can get at those from any of the environments I just mentioned. If you're in a servlet, for example, then you could do this:

public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {

String challenge = req.getParameter("recaptcha_challenge_field");
String response = req.getParameter("recaptcha_response_field");
String remoteAddr = req.getRemoteAddr();
ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
...etc...
}

Hopefully that helps to clarify. Again the idea is that this code processes the form data coming from the reCAPTCHA form and so you put the code wherever it is you're going to capture that data.
By Willie Wheeler on May 4, 2008 at 8:15 PM PDT
I'm totally lost on the integration of this thing. Do you drop the recaptcha script in the same form as your fields. The thing that im filling out right now to send this question is EXACTLY what I want to do. Sorry, im a little slow i guess.
By Sam on May 27, 2008 at 5:36 PM PDT
Heh, I can see that I need to provide a download or something. I'll try to get one up this weekend. :-)
By Willie Wheeler on May 27, 2008 at 9:06 PM PDT
OK, I've created a download for this. It's at

http://wheelersoftware.s3.amazonaws.com/articles/recaptcha-java/recaptcha-example.zip

Hope this helps.
By Willie Wheeler on Jun 1, 2008 at 11:54 AM PDT
Thanks for posting that example. It helped cleared a lot of confusion. I have a few more problems though. When I fill out the example form it tries to find "recaptcha-example\web\postcomment" but there is no such file. Why is this? What should I do to fix it? Thanks!
By Ben Chevoor on Jul 2, 2008 at 9:23 AM PDT
Hi,
I followed all the instruction you were giving in your post [http://wheelersoftware.com/articles/recaptcha-java.html]
about "How to reCAPTCHA Your Java Application"
It seems like it working up to the minute you submit and then I get this message:
"<?php
$request_method = $_SERVER["REQUEST_METHOD"];
if($request_method == "GET"){
$query_vars = $_GET;
} elseif ($request_method == "POST"){
$query_vars = $_POST;
}
reset($query_vars);
$t = date("U");

$file = $_SERVER['DOCUMENT_ROOT'] . "/../data/gdform_" . $t;
$fp = fopen($file,"w");
while (list ($key, $val) = each ($query_vars)) {
fputs($fp,"<GDFORM_VARIABLE NAME=$key START>\n");
fputs($fp,"$val\n");
fputs($fp,"<GDFORM_VARIABLE NAME=$key END>\n");
if ($key == "redirect") { $landing_page = $val;}
}
fclose($fp);
if ($landing_page != ""){
header("Location: http://".$_SERVER["HTTP_HOST"]."/$landing_page");
} else {
header("Location: http://".$_SERVER["HTTP_HOST"]."/");
}


?>"

Please, can you let me know what is wrong?
Thanks in advance!
I will appreciate any help.

lana.
By Lana on Jul 3, 2008 at 1:26 PM PDT
@Ben: You're welcome. Not sure what's going on with that--it shouldn't be trying to find a file. If you look at web.xml there is a servlet mapping with the URL pattern /postcomment. That should send the request to CommentServlet. Without seeing what you have it's hard for me to know what's happening. :-| What servlet container are you running?

@Lana: Same question I had for Ben--what servlet container and web server are you using? And can you provide any insight into why it (I assume the web server?) might be returning PHP?
By Willie Wheeler on Jul 3, 2008 at 5:39 PM PDT
I am getting "java.lang.NoSuchMethodError" while trying to check answer.
ReCaptchaResponse objCaptchaResponse = objCaptcha.checkAnswer(strRemoteAddr, strChallengeField, strResponseField);
By Joe on Jul 10, 2008 at 7:21 AM PDT
To add to my previous comment posted. The .jar is downloaded and class path is set. Stack dump says the error occured at net.tanesha.recaptcha.ReCaptchaImpl.checkAnswer(ReCaptchaImpl.java:67) . No such method.
By joe on Jul 10, 2008 at 7:25 AM PDT
Hi Joe. I opened up the ReCAPTCHA source and looked at the line in question. It is a call to String.split(), which the Javadoc says was introduced in Java 1.4. So I'm guessing you're using pre-1.4. If so you would need to upgrade to a newer version of Java to use the ReCAPTCHA client.
By Willie Wheeler on Jul 10, 2008 at 8:14 AM PDT
Thanks for clarifying "No Such method" error. JDK used was older version.
By joe on Jul 11, 2008 at 4:52 AM PDT
Hi Willie...
Everything works fine to me with JSP, Struts and JSTL... but... if I put the <html:submit button (or any other thing) below the rechapta then the button (or whatever I put there) doesn't show... any idea??? I tried for hours thinking it could be a CSS problem..
By maria on Jul 15, 2008 at 12:25 PM PDT
Hey Maria. Did you remember to add the taglib declarations to the top of the JSP?
By Willie Wheeler on Jul 15, 2008 at 5:24 PM PDT
Hey Willie! I have all the taglibs I need it before using the reCaptcha.. an I didn't read in any recaptcha page I need any taglib more for showing the recaptha isnt it???.. The button shows up perfectly if I put it over the recaptcha but not below... It happends the same with words, more divs or whatever...
Yesterday I also tried the example you gave, just your html in my application... and it happened just the same... button before recaptcha ok, button below not showing..
Thanks for your help willie!! ;)
By Maria on Jul 16, 2008 at 3:00 AM PDT
I am guessing the issue is a missing quote or something like that. Go ahead and send me the file and I'll be happy to take a quick look.
By Willie Wheeler on Jul 16, 2008 at 7:40 AM PDT
Hello Willie,

I truly admire your patience to answer each and every question with the same degree of enthusiasm.

Thanks a lot for this post.

I could get my reCaptcha working in just an hour after going thru post.
By Rohith on Jul 17, 2008 at 3:56 AM PDT
hey guys, i've been reading the post, but im lost. I know java and i understand the code. I just don't know how to link it to my form. My client does not want to use php....don't ask...im mad about that. I Have this on my html page. I can see the recaptcha,but i don't know how to verify it.

thanks in advance.

<form action="../asp/contact.asp" method="post" name="form">

<script type="text/javascript"
src="http://api.recaptcha.net/challenge?k=<6LfiGQMAAAAAAMatqfHPhTtHHlwSxAiw0kcqDOz3>">
</script>

<noscript>
<iframe src="http://api.recaptcha.net/noscript?k=<6LfiGQMAAAAAAMatqfHPhTtHHlwSxAiw0kcqDOz3>"
height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40">
</textarea>
<input type="hidden" name="recaptcha_response_field"
value="manual_challenge">
</noscript>
By Francisco Gomez on Sep 11, 2008 at 3:53 PM PDT
@Francisco: Take the angle brackets out of the public key. I.e., it should be

k=6LfiGQMAAAAAAMatqfHPhTtHHlwSxAiw0kcqDOz3

not

k=<6LfiGQMAAAAAAMatqfHPhTtHHlwSxAiw0kcqDOz3>
By Willie Wheeler on Sep 11, 2008 at 4:04 PM PDT
ok, i have made the change. How do i verify it now?
By Francisco Gomez on Sep 15, 2008 at 8:51 AM PDT
Well, if the reCAPTCHA shows up on the page then it's working. If not then not. :-)
By Willie Wheeler on Sep 15, 2008 at 9:55 AM PDT
yes it does display, but it doesn't verify.
By Francisco Gomez on Sep 15, 2008 at 7:03 PM PDT
Well, the article is my attempt to answer your question. If you can provide a little more information on what you are doing and what isn't working I might be able to assist.
By Willie Wheeler on Sep 15, 2008 at 7:30 PM PDT
Hi Willie.. I tried your sample zip file and just like Ben (7/2/2008) post, I am having the same error once I submit the form.

Once i hit submit, it finds its way to going to "recaptcha-example\web\postcomment", are you re-directing the form here and should have a file here?

All i did is download the file, upload it to my hosting and put in my public key, then tested to submit, then got this error. Any idea?

Also my form is plain HTML.. thanks!
By jason on Sep 21, 2008 at 6:37 PM PDT
Hey Willie, any input or help?
By Jason on Sep 27, 2008 at 3:17 PM PDT
Hi Jason. Like I said to Ben, I'm not really sure what's going on. I just re-downloaded the example to try it out and it still works on my end. I'm using Tomcat 6 if that helps. Basically the form should be posting to recaptcha-example/postcomment, and then that URL should be mapped to the CommentServlet in the web.xml file. The first thing I'd try is adding debug logging to the doPost() method to see whether your controller is even getting the request. You might also try changing the URL to postcomment.do instead of postcomment. (For example if your host has Apache in front of your servlet container then maybe the Apache config isn't passing extensionless URLs to your servlet container.) If you do this, make sure you update both the form action and the web.xml file. Anyway let me know if that works; if it doesn't, I can try to help if you provide more information on your debugging efforts.
By Willie Wheeler on Sep 27, 2008 at 4:07 PM PDT
Hi Willie, thanks for posting this article, got a question, I am able to display the captcha, but not able to get the values for the fields, when i try to retrieve the parameters from the servlet request, I get null values, any ideas?
Many thanks.
By Rafa on Sep 30, 2008 at 4:21 AM PDT
Hi Rafa. Can you post the relevant part of your code?
By Willie Wheeler on Sep 30, 2008 at 8:13 AM PDT
Hi, thanks, sorted out that one, was not putting the code btw the form tags :(, but now I am getting this error when testing locally,
invalid-site-private-key
should not the private key work for localhost as well?
By Rafa on Oct 1, 2008 at 3:32 AM PDT
Actually, my mistake again, a typo, now everything is working fine.
Thank you!
By Rafa on Oct 1, 2008 at 3:40 AM PDT
I am trying with the sample code and replaced the index.html with the public key (twice) and the CommentServlet with the private key. It started the captcha but when i submit it to verify the captcha it always returns me false. Any idea?
By Sayem on Oct 7, 2008 at 10:12 PM PDT
@Sayem: Is your reCAPTCHA'ed app behind a proxy server?
By Willie Wheeler on Oct 7, 2008 at 10:31 PM PDT
@Willie: Thanks for you reply. I found the reason. It's an issue with the IPV6. So when it was trying to get the remote address it was returning 0.0.0.0.0.1 something like that. I replaced them with 127.0.0.1 and it is working now.
By Sayem on Oct 8, 2008 at 8:02 AM PDT
the checkAnswer() method only checks the first word. When i entered a correct first word and whatever i enter for the second word it shows successfull.Am i missing something?
By Sayem on Oct 9, 2008 at 7:59 AM PDT
Hi Sayem. The way reCAPTCHA works is that they serve two words, one of which is known to reCAPTCHA and one of which is not. So you only need to get one word right, since reCAPTCHA doesn't even know what the other word is. (You can read more about this on their website. There's a logic behind it.) I'd be surprised if it turned out that the known word is always the first one but for all I know that could be the case.
By Willie Wheeler on Oct 9, 2008 at 7:29 PM PDT
@Willie: Thanks for your response. I was wondering why it was showing two letters. It might have some reason but most of the client might not understand them.Thanks again.
By Sayem on Oct 10, 2008 at 2:11 AM PDT
Hi Sayem. Yeah, it's actually one of the weaknesses of reCAPTCHA--the fact that they show two words. From the end user's point of view CAPTCHAs are pure overhead; they don't benefit the end user directly at all. (There's an indirect benefit of course in that the forum or whatever doesn't have as much spam.) And so by having two words we've essentially doubled the end user's pain. Like you say, most users won't understand why there are two words. Frankly even if they did understand why, it wouldn't matter. It's still a bother to fill it out.
By Willie Wheeler on Oct 11, 2008 at 1:24 AM PDT

Post a comment

Your name:
Your e-mail address (won't be displayed):
Your web site (optional):
example: www.xyz.com
Your comment:
Preview:
By You
Please help us reduce comment spam:
Spring in Practice
My brother and I are writing Spring in Practice for Manning!

What's New?

2008-12-14 - We've just submitted a few more chapters of the book for review, so we're about halfway done.
2008-10-20 - I've added a new mailing list feature to the site. Sign up to receive e-mail updates about new articles.
2008-09-30 - We've released chapter 4 (User registration) and chapter 5 (Authentication) of Spring in Practice.
2008-09-11 - By popular demand, I've added an RSS feed to the site.
Home | Consulting | Tech Articles | Mailing List | Contact | Spring Blog
Copyright © 2008 Wheeler Software, LLC.