Wicket + Kaptcha integration example
Kaptcha is simple and easy to use Java library to produces a captcha image validation. In this tutorial, we show you how to integrate Kaptcha with Wicket framework, via Spring.
Libraries used :
- Kaptcha v2.3.2
- Wicket v1.4.17
- wicket-spring v1.4.17
- Spring v3.0.5.RELEASE
This article is mainly describes how to integrate Kaptcha with Wicket framework, for Wicket + Spring, please refer to this “Wicket + Spring integration example“.
1. Get Kaptcha
According to this thread, the owner don’t like Maven, so you have to install the library into your local Maven repository manually.
1. Get Kaptcha library here http://code.google.com/p/kaptcha/
2. Issue below Maven command to install it manually.
mvn install:install-file -Dfile=c:\kaptcha-2.3.2.jar -DgroupId=com.google.code -DartifactId=kaptcha -Dversion=2.3.2 -Dpackaging=jar
3. Later, you can includes kaptcha in your pom.xml file.
File : pom.xml
<dependency> <groupId>com.google.code</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
2. DefaultKaptcha via Spring
Create a Spring bean for “DefaultKaptcha“, named captchaProducer.
File : applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha"> <property name="config"> <bean class="com.google.code.kaptcha.util.Config"> <constructor-arg type="java.util.Properties" value="null"> </constructor-arg> </bean> </property> </bean> </beans>
3. CaptchaImage
Create a CaptchaImage class which extends Wicket’s NonCachingImage and using DynamicImageResource class to generate captcha image dynamically.
File : CaptchaImage.java
package com.mkyong.user; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import javax.servlet.http.HttpServletRequest; import org.apache.wicket.Request; import org.apache.wicket.RequestCycle; import org.apache.wicket.markup.html.image.NonCachingImage; import org.apache.wicket.markup.html.image.resource.DynamicImageResource; import org.apache.wicket.protocol.http.WebRequest; import org.apache.wicket.spring.injection.annot.SpringBean; import com.google.code.kaptcha.Constants; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; public class CaptchaImage extends NonCachingImage { private static final String CAPTCHA_PRODUCER = "captchaProducer"; // inject via Spring @SpringBean private DefaultKaptcha captchaProducer; // private DefaultKaptcha captchaProducer; public CaptchaImage(String id) { super(id); setImageResource(new DynamicImageResource() { public byte[] getImageData() { ByteArrayOutputStream os = new ByteArrayOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os); try { BufferedImage bi = getImageCaptchaService(); encoder.encode(bi); return os.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } }; private BufferedImage getImageCaptchaService() { Request request = RequestCycle.get().getRequest(); HttpServletRequest httpRequest = ((WebRequest) request) .getHttpServletRequest(); String capText = captchaProducer.createText(); // store the text in the session httpRequest.getSession().setAttribute( Constants.KAPTCHA_SESSION_KEY, capText); // create the image with the text BufferedImage bi = captchaProducer.createImage(capText); return bi; } }); } }
4. CaptchaValidator
Create a custom validator, named “CaptchaValidator“, use to validate user input and compare with the Kaptcha generated code.
File : CaptchaValidator.java
package com.mkyong.user; import javax.servlet.http.HttpServletRequest; import org.apache.wicket.Request; import org.apache.wicket.RequestCycle; import org.apache.wicket.protocol.http.WebRequest; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.validator.AbstractValidator; public class CaptchaValidator extends AbstractValidator<String> { private static final long serialVersionUID = 1L; private String INVALID_CODE = "captcha.invalid"; public void onValidate(IValidatable validatable) { String kaptchaReceived = (String) validatable.getValue(); Request request = RequestCycle.get().getRequest(); HttpServletRequest httpRequest = ((WebRequest) request) .getHttpServletRequest(); String kaptchaExpected = (String) httpRequest.getSession() .getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY); if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) { error(validatable); } } // validate on numm value as well @Override public boolean validateOnNullValue() { return true; } @Override protected String resourceKey() { return INVALID_CODE; } }
File : package.properties
captcha.invalid = Incorrect answer, type words in image again!
5. Wicket Components
Integrate Kaptcha with Wicket components.
package com.mkyong.user; import org.apache.wicket.PageParameters; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.model.PropertyModel; public class KaptchaPage extends WebPage { private String captchaInput; public KaptchaPage(final PageParameters parameters) { final CaptchaImage captchaImage = new CaptchaImage("kaptchaImage"); captchaImage.setOutputMarkupId(true); TextField<String> captchaTF = new TextField<String>("captcha", new PropertyModel<String>(this, "captchaInput")); captchaTF.add(new CaptchaValidator()); Form<?> form = new Form<Void>("form") { @Override protected void onSubmit() { info("Image words are correct!!!"); }; }; form.add(new AjaxFallbackLink("link") { @Override public void onClick(final AjaxRequestTarget target) { captchaImage.detach(); if (target != null) { target.addComponent(captchaImage); } else { // javascript is disable } } }.add(captchaImage)); form.add(captchaTF); add(form); add(new FeedbackPanel("feedback")); } }
captchaImage.detach(); is enables generate a new captcha image dynamically while user click on the captcha image.<html> <head> <style> .feedbackPanelINFO { color: green; } .feedbackPanelERROR { color: red; } </style> </head> <body> <h1>Wicket + Kaptcha integration example</h1> <div wicket:id="feedback"></div> <form wicket:id="form"> <a wicket:id="link" title="Refresh image words"> <img wicket:id="kaptchaImage" /></a> <br> <label>Type the image words :</label> <input type="text"wicket:id="captcha"> <input type="submit" value="Submit" /> </form> </body> </html>
6. Demo
Start and visit – http://localhost:8080/WicketExamples/
If imput is incorrect :

If input is correct :

References
- Kaptcha official website
- Wicket + Spring integration example
- Include library into local Maven repository