Wiki News Projects Sources Tasks New Task Reports
ImplementingCaptchaWithJSF
Page Info Get as PDF

Implementing Captcha with JSF

Top Announcement: 10-June-2008 EmForge-0.24 with MyLyn and CVS support released! See News 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.

Captcha Generation

For generating Captcha we used JCaptcha library. It is quite powerfull library with many features - and one of them - generation and validation of captcha images.

Adding JCaptcha into Project.

Since both: EmForge and JCaptcha are Mavenbased 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.

Including Captcha into JSF Page

For rendering Captcha in JSF page we used paint2D 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:

  1. define id, used for storing generated text;
  2. Add ImageCaptchaService;
  3. Add Method, generated image
  4. And accessor to secure text, entered by user
  5. And method to generate random string:
(only part of class is here - whole code you can see here: RegisterController.java)

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.

Validating Captcha

For validating Captcha we implemented special validator - CaptchaValidator (assigned to secureText input):

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
Comments (1)
Posted by akakunin 3 months ago
Spring Framework's captcha component - another example of ready-to use Captcha Solution for JSF
Login to add comment