ImplementingCaptchaWithJSF
|
for details.
In registration form, used in EmForge we needed Captcha-implementation to save it from robots. I looked into internet -but did not found any ready solution for JSF-based project - so, here is one.
library. It is quite powerfull library with many features - and one of them - generation and validation of captcha images.
are Maven
based projects
and I found jcaptcha in the public maven repositories it was quite easy to add jcaptcha into project - just add it into projects dependency inpom.xml
<dependency>
<groupId>com.octo.captcha</groupId>
<artifactId>jcaptcha-all</artifactId>
<version>1.0-RC6</version>
</dependency>
If your project is not maven-based - just download jcaptcha jar and add into your WEB-INF/libs folder.
control from rich-faces:
<h:form id="registerForm">
<h:panelGrid columns="2" styleClass="properties-table">
...
<!-- CaptchaImage -->
<rich:paint2D id="captcha"
width="#{registerController.captchaWidth}"
height="#{registerController.captchaHeight}"
format="jpeg"
paint="#{registerController.paintCaptcha}"
data="#{registerController.randomString}"/>
<h:outputText value="Text on Image:"/>
<h:panelGroup>
<h:inputText id="secureText"
value="#{registerController.secureText}"
size="40"
required="true">
<emforge:captchaValidator/>
</h:inputText>
<h:message for="secureText" errorClass="errorMessage"/>
</h:panelGroup>
<h:commandLink action="#{registerController.submit}">
<h:outputText value="Submit"/>
</h:commandLink>
</h:panelGrid>
</h:form>
In RegisterController we need to:
)
public class RegisterController extends BaseControllerImpl implements InitializingBean {
public static final String CAPTCHA_ID = "EmForge";
private ImageCaptchaService m_imageCaptchaService;
private String m_secureText;
public void setImageCaptchaService(ImageCaptchaService i_imageCaptchaService) {
m_imageCaptchaService = i_imageCaptchaService;
}
public String getSecureText() {
return m_secureText;
}
public void setSecureText(String i_secureText) {
m_secureText = i_secureText;
}
protected BufferedImage generateCaptcha() {
try {
return m_imageCaptchaService.getImageChallengeForID(CAPTCHA_ID);
} catch (Exception ex) {
logger.error("Cannot generate captcha image", ex);
return null;
}
}
public int getCaptchaWidth() {
return 200;
}
public int getCaptchaHeight() {
return 100;
}
/** Paints Captcha Image
*
* @param g2d
* @param obj
*/
public void paintCaptcha(Graphics2D g2d, Object obj) {
BufferedImage secureImage = generateCaptcha();
try {
g2d.setClip(0,0,secureImage.getWidth(), secureImage.getHeight());
g2d.drawImage(secureImage, 0,0, null);
} catch (Exception ex) {
logger.error("Cannot generate captcha image", ex);
}
}
/** Generates Random Text for displaying on the image */
public String getRandomString() {
String str=new String("QAa0bcLdUK2eHfJgTP8XhiFj61DOklNm9nBoI5pGqYVrs3CtSuMZvwWx4yE7zR");
StringBuffer sb=new StringBuffer();
Random r = new Random();
int te=0;
for(int i=1;i<=6;i++){
te=r.nextInt(62);
sb.append(str.charAt(te));
}
return sb.toString();
}
Since JCaptcha supports integration with spring-framework, ImageCaptchaService may be defined as spring bean and passed int registerController as param (part of beans configuration):
<beans>
...
<bean id="imageCaptchaService" class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService" scope="session"/>
<bean id="registerController" class="ru.emdev.EmForge.security.RegisterController" scope="request">
...
<property name="imageCaptchaService" ref="imageCaptchaService"/>
</bean>
...
</beans>
Now, you will able to generate and render the captcha image, as well as enter the text.
package ru.emdev.EmForge.security.web.validator;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import ru.emdev.EmForge.security.RegisterController;
import com.octo.captcha.service.image.ImageCaptchaService;
/**
* Validates inputed name for a project
*
* @author mchirkov
*/
public class CaptchaValidator implements Validator {
public static final char[] INVALID_CHARS = new char[] {'*', '@', '/', '\\'};
public static final int MAX_NAME_LENGTH = 50;
public static final String VALUEREQUIRED_ERROR_MESSAGE = "Value is required.";
public static final String TEXTNOTNATCH_ERROR_MESSAGE = "Text not match text on image";
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
String val = StringUtils.strip((String)value);
if (StringUtils.isEmpty(val)) {
throw new ValidatorException(
new FacesMessage(VALUEREQUIRED_ERROR_MESSAGE, VALUEREQUIRED_ERROR_MESSAGE)
);
}
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
WebApplicationContext appContext =
WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
assert appContext != null;
ImageCaptchaService imageCaptchaService = (ImageCaptchaService)appContext.getBean("imageCaptchaService");
if (!imageCaptchaService.validateResponseForID(RegisterController.CAPTCHA_ID, val)) {
throw new ValidatorException(
new FacesMessage(TEXTNOTNATCH_ERROR_MESSAGE, TEXTNOTNATCH_ERROR_MESSAGE));
}
}
}
ImageCaptchaService, stored inside itself the text, generated in image, so, here, we simple get this bean from Spring Context, and validate it
So, now we only need to declare validator in faces-config.xml:
<validator>
<validator-id>captchaValidator</validator-id>
<validator-class>ru.emdev.EmForge.security.web.validator.CaptchaValidator</validator-class>
</validator>
and define validator tag in our facelets taglib (emforge.taglib.xml)
<tag>
<tag-name>captchaValidator</tag-name>
<validator>
<validator-id>captchaValidator</validator-id>
</validator>
</tag>
What is all. Enjoy!
One Note: If you have any comments - please, post them to SourceForge forum
- while we do not have own forum yet
| Last Modified by akakunin 4 weeks ago |