Recently a bot figured out how to submit my comment form. It was pretty funny I had more than 700 comments telling me how interesting my post on how to install a jar in maven was. I decided to add captcha support and while I was at it, make the comment form use DWR for submission so that the page didn’t refresh etc.
I have created a little example application demonstrating how I integrated JCaptcha with DWR. The example is also (hopefully) serves as an example of how to use the new Spring Namespace support that has been added to DWR. I think if you’re using DWR with Spring 2 and you want to keep all your configuration within Spring, you should use the Namespace support for configuring DWR. It’s just easier. Please note this example only runs in a Java 5 environment and that if you’re using DWR 2.0.rc2 there is a small issue with using Spring 2.0.1 or better so I’ve had to use Spring 2.0. This issue is fixed in CVS HEAD so if you want to build DWR yourself from source you should have no problems with Spring 2.0.1 or 2.0.2.
DWR Configuration in Spring
To keep things simple, in this example all the DWR configuration is done in the spring-catpcha-controllers.xml spring configuration. First we register the DwrController using:
<dwr:controller id="dwrController" debug="false" >
Now we need to tell DWR what domain objects we’d like to expose using javascript. DWR does this using Converters. In this case I’m really only exposing the Comment domain object:
<dwr:convert type="bean" class="com.kuripai.example.domain.Comment">
<dwr:include method="id" />
<dwr:include method="title" />
<dwr:include method="postedBy" />
<dwr:include method="website" />
<dwr:include method="email" />
<dwr:include method="body" />
<dwr:include method="createdAt" />
<dwr:include method="humanResponse" />
</dwr:convert>
The next step is to configure and expose the service that saves comments. I’ve created a very simple WeblogService for this purpose which just saves the comments into a java.util.List. Obviously a production version would save the comments in a database or similar. Here is the configuration:
<bean id="weblogService" class="com.kuripai.example.service.WeblogService" >
<dwr:remote javascript="weblogService">
<!-- Methods that are allowed to be exposed via javascript -->
<dwr:include method="getComments" />
<dwr:include method="saveComment" />
<!-- Filter to handle Captcha -->
<dwr:filter class="com.kuripai.example.dwr.captcha.CaptchaAjaxFilter" />
</dwr:remote>
<property name="validators">
<list>
<ref local="captchaValidator" />
<bean class="com.kuripai.example.domain.CommentValidator" />
</list>
</property>
</bean>
As you can see you configure your service as normal with the additional
public Errors saveComment(Comment comment) {
Errors errors = new BindException(comment, "comment");
for (int i = 0; i < validators.length; i++) {
ValidationUtils.invokeValidator(validators[i], comment, errors);
}
if (!errors.hasErrors()) {
comments.add(comment);
comment.setId(Long.valueOf((long) comments.size()));
}
return errors;
}
The org.directwebremoting.AjaxFilter
You might have notices the
public Object doFilter(Object object, Method method, Object[] params, AjaxFilterChain chain) throws Exception {
if (logger.isDebugEnabled()) {
logger.info("Processing method '" + method.getName() + "' on service '" +
object + "'");
}
HttpSession session = WebContextFactory.get().getSession();
CaptchaResponseHolder.setCaptchaId(session.getId());
Object reply = chain.doFilter(object, method, params);
return reply;
}
The CaptchaValidator then is a regular org.springframework.validation.Validator:
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, CAPTCHA_FIELD_NAME,
"form.error.required",
new Object[] { new DefaultMessageSourceResolvable(
"form.captcha") }, "Please enter the validation word");
if (errors.getFieldError(CAPTCHA_FIELD_NAME) != null) {
return;
}
CaptchaAware captchaAware = (CaptchaAware) target;
if (!isCaptchaValid(captchaAware.getHumanResponse())) {
errors.rejectValue(CAPTCHA_FIELD_NAME, "form.error.captchaInvalid",
"Please enter the security word again");
}
}
private boolean isCaptchaValid(Object response) {
String captchaId = CaptchaResponseHolder.getCaptchaId();
if (logger.isDebugEnabled()) {
logger.debug("Validating captcha response '" + response
+ "' for captchaId '" + captchaId + "'");
}
Boolean isValid = captchaService.validateResponseForID(captchaId,
response);
return isValid.booleanValue();
}
Running the Example
That’s pretty much it. I’ve zipped up the source code for the example here → (I will put this up in the next few days). Unzip this into a directory of your choice and type mvn tomcat:run after completing the steps below providing of course you have maven 2 installed. You should also be able to import the project into eclipse to take a look at it.
Installing JCaptcha and Other Required Libraries in your Maven Repository
Unfortunately there seems to be a problem with the version of JCaptcha in on ibiblio. So you will need download and install the jar in you maven repository yourself following these steps (assuming you have Maven 2 installed):
1. Download jcaptcha-1.0-RC3 binary from the projects download page.
2. Unzip the downloaded jcaptcha binary and cd into the directory where you unzipped it.
3. Install the jar using this command:
mvn install:install-file -DgroupId=jcaptcha -DartifactId=jcaptcha-all \
-Dversion=1.0-RC3 -Dpackaging=jar -Dfile=jcaptcha-all-1.0-RC3.jar
Running the Example
You should now be ready to run the example. From the command line, cd into the directory where you downloaded the example. Type:
mvn clean tomcat:run
Maven should download a whole bunch of jar files including a enough to run an embedded version of Tomcat. Which will start up and run. Navigate to http://localhost:8080/dwr-captcha/example/comments.html and you should see a regular looking comment form with captcha that never refreshes.
blog comments powered by Disqus