JSF 2.2 Captcha example with refresh button

This tutorial explains how to implement captcha in JSF page. Jsf Primefaces has inbuilt support of recaptcha feature. Recaptcha can be implemented using <p:captcha/> tag.  In this tutorial, let us create our own captcha in java and implement in JSF page. Yo can also view my earlier tutorial how to implement captcha in Spring Mvc.

Let use take an example of creating user registration page that accepts captcha so that user creation through automated tool can be prevented

Technology used in this project:

JSF 2.2

Eclipse IDE

Apache Tomcat 7.0 or 8.0

Java 1.8

Jars required for  JSF:

javax.faces.jar

jstl-1.2.jar

Project Directory Structure:

jsf captcha

Now let us see the steps to implement captcha in registration page in JSF.

Brief Steps:

1. Create a Registration page with captcha. JSF tag to display captcha image is
2. Create managed bean for the registration page.
2. Write java code (CaptchaServlet.java) for generating captcha text and image, store text on session and return image as response outputstream.
3. Write captcha validator code (CaptchaValidator.java) to verify whether the captcha text entered by the user and stored in the session matches or not.
<f:validator validatorId=”captchaValidator” />– this registers a named validator class (i.e. CaptchaValidator) on the UIComponent (captchaText input) for captcha validation

Steps in Detail:

Step 1: Design a Registration page having the following fields/components. Add/ Remove field(s) as per your requirement.
1. Email Id* as user id
2. Name*
3. Password* & Confirm Password*
4. Mobile No*
5. Captcha text
6. Image tag to display captcha image.
7. refresh button to regenerate captcha

JSF code (registration.xhtml):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html">

<h:head>
	<style type="text/css">
</style>
</h:head>
<h:form id="registration" prependId="false">



	<p>
		<h:messages id="messages" />
	</p>

	<table>

		<tr>
			<td><h:outputLabel for="txtEmailId" value="Enter your Email ID*" />
				<h:inputText id="txtEmailId" required="true"
					value="#{registerBean.emailId}" label="Email Id"
					validatorMessage="Email Id is not Valid">
					<f:validateRegex
						pattern="^[_\w-\+]+(\.[_\w-]+)*@[\w-]+(\.[\w]+)*(\.[A-Za-z]{2,})$"></f:validateRegex>
				</h:inputText></td>
		</tr>

		<tr>
			<td><h:outputLabel for="txtNameId" value="Enter your Name*" />
				<h:inputText id="txtNameId" required="true"
					value="#{registerBean.name}" label="Name">
				</h:inputText></td>
		</tr>


		<tr>
			<td><h:outputLabel for="txtPasswordId" value="Enter Password*" />
				<h:inputSecret id="txtPasswordId" required="true"
					value="#{registerBean.password}" label="Password">
				</h:inputSecret></td>
		</tr>


		<tr>
			<td><h:outputLabel for="txtConfirmPasswordId"
					value="Enter Confirm Password*" /> <h:inputSecret
					id="txtConfirmPasswordId" required="true"
					value="#{registerBean.confirmPassword}" label="Confirm Password">
				</h:inputSecret></td>
		</tr>


		<tr>
			<td><h:outputLabel for="txtMobileNo"
					value="Enter your Mobile No*" /> <h:inputText id="txtMobileNo"
					required="true" label="Mobile No" value="#{registerBean.mobileNo}"
					maxlength="10"
					validatorMessage="Enter a valid Mobile number having 10 digits">
					<f:validateRegex pattern="\d{10}"></f:validateRegex>
				</h:inputText></td>
		</tr>



		<tr>
			<td><h:outputLabel for="txtCaptchaId"
					value="Enter text shown in the Image*" /> <h:inputText
					id="txtCaptchaId" required="true"
					value="#{registerBean.captchaText}" label="Enter Captcha"
					placeholder="Enter Security Code" autocomplete="false">
					<f:validator validatorId="captchaValidator" />
				</h:inputText></td>
			<td align="center"><h:graphicImage id="imgCaptchaId"
					value="captcha.jpg" /> <h:commandLink
					onclick="document.getElementById('imgCaptchaId').src = 'captcha.jpg?' + Math.random();  return false">
					<h:graphicImage library="images"
						value="#{facesContext.externalContext.requestContextPath}/../images/refresh.png" />
				</h:commandLink></td>
		</tr>

		<tr>
			<td align="center"><h:commandButton value="Submit"
					action="#{registerBean.createNewUser}" ajax="false">
				</h:commandButton></td>
		</tr>
	</table>

</h:form>

</html>

Step 2: Create g managed bean for the registration page.

RegisterBean.java
package net.javaonline.user.registration.bean;

import java.io.Serializable;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

@ManagedBean
@RequestScoped
public class RegisterBean implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String emailId;
	private String name;
	private String mobileNo;
	private String password;
	private String confirmPassword;
	private String captchaText;

	public String getEmailId() {
		return emailId;
	}

	public void setEmailId(String emailId) {
		this.emailId = emailId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getMobileNo() {
		return mobileNo;
	}

	public void setMobileNo(String mobileNo) {
		this.mobileNo = mobileNo;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getConfirmPassword() {
		return confirmPassword;
	}

	public void setConfirmPassword(String confirmPassword) {
		this.confirmPassword = confirmPassword;
	}

	public String getCaptchaText() {
		return captchaText;
	}

	public void setCaptchaText(String captchaText) {
		this.captchaText = captchaText;
	}

	public String createNewUser() {

		if (getPassword().compareTo(getConfirmPassword()) != 0) {
			setCaptchaText("");
			FacesContext
					.getCurrentInstance()
					.addMessage(
							"messages",
							new FacesMessage(
									FacesMessage.SEVERITY_INFO,
									"Password and Confirm Password does not match ",
									""));
			return "failure";
		}
		
		if (getEmailId().equals("guest@abc.com") && getPassword().equals("guest123"))
			return "success";
		else
			return "failure";
	}

}

Step 3: Servet code to generate Captcha image and returns the image as response outputstream

CaptchaServlet.java:
package net.javaonline.captcha;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.servlet.http.*;
import javax.servlet.*;

import java.io.*;
import java.awt.*;
import net.javaonline.captcha.util.Util;

public class CaptchaServlet extends HttpServlet {

	public static final String FILE_TYPE = "jpeg";

	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);
		response.setHeader("Pragma", "no-cache");
		response.setDateHeader("Max-Age", 0);

		String captchaStr = "";

		captchaStr = Util.generateCaptchaText(6);

		try {

			int width = 100;
			int height = 40;

			Color bg = new Color(0, 255, 255);
			Color fg = new Color(0, 100, 0);

			Font font = new Font("Arial", Font.BOLD, 20);
			BufferedImage cpimg = new BufferedImage(width, height,
					BufferedImage.OPAQUE);
			Graphics g = cpimg.createGraphics();

			g.setFont(font);
			g.setColor(bg);
			g.fillRect(0, 0, width, height);
			g.setColor(fg);
			g.drawString(captchaStr, 10, 25);

			HttpSession session = request.getSession(true);
			session.setAttribute("CAPTCHA", captchaStr);

			OutputStream outputStream = response.getOutputStream();

			ImageIO.write(cpimg, FILE_TYPE, outputStream);
			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

}

Code to generate random Text (i.e. captcha text generation)

generateCaptchaText method:
package net.javaonline.captcha.util;

public class Util {


	public static String generateCaptchaText(int captchaLength) {

		String saltChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
		StringBuffer captchaStrBuffer = new StringBuffer();
		java.util.Random rnd = new java.util.Random();

		// build a random captchaLength chars salt
		while (captchaStrBuffer.length() < captchaLength) {
			int index = (int) (rnd.nextFloat() * saltChars.length());
			captchaStrBuffer.append(saltChars.substring(index, index + 1));
		}

		return captchaStrBuffer.toString();

	}

	
}

Step 4: Write validator code to verify whether the user entered captcha text matches with captcha stored in the session.

CaptchaValidator.java:
package net.javaonline.captcha;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.servlet.http.HttpServletRequest;

@FacesValidator(value = "captchaValidator")
public class CaptchaValidator implements Validator {

	@Override
	public void validate(FacesContext context, UIComponent component,
			Object value) throws ValidatorException {
		String captchaEntered = (String) value;

		System.out.println("captcha " + captchaEntered);

		FacesMessage message = null;

		try {

			if (captchaEntered == null || captchaEntered.isEmpty())
				message = new FacesMessage(
						"Please Enter Security Code shown in the image box");

			else {
				HttpServletRequest request = (HttpServletRequest) FacesContext
						.getCurrentInstance().getExternalContext().getRequest();
				javax.servlet.http.HttpSession session = request.getSession();
				String captcha = (String) session.getAttribute("CAPTCHA");
				System.out.println("captcha G " + captcha);
				if (!captchaEntered.equals(captcha)) {
					message = new FacesMessage("Captcha is invalid");
				}
			}

			if (message != null)
				throw new ValidatorException(message);

		} catch (Exception ex) {
			throw new ValidatorException(new FacesMessage(ex.getMessage()));
		}

	}

}

Create JSF page for Acknowledgement for User creation

registration_ack.xhtml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:p="http://primefaces.org/ui">

<h:head>
	<title>JSF 2.2 Registration with Captcha</title>
</h:head>
<h:body bgcolor="white">


	<h2>You are registered with us successfully</h2>

</h:body>
</html>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>JSF Captcha</display-name>
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>/faces/*</url-pattern>
	</servlet-mapping>
	<context-param>
		<param-name>facelets.SKIP_COMMENTS</param-name>
		<param-value>true</param-value>
	</context-param>
	<context-param>
		<param-name>primefaces.THEME</param-name>
		<param-value>glass-x</param-value>
	</context-param>


	<servlet>
		<servlet-name>CaptchaServlet</servlet-name>
		<servlet-class>net.javaonline.captcha.CaptchaServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>CaptchaServlet</servlet-name>
		<url-pattern>/captcha.jpg</url-pattern>
	</servlet-mapping>


</web-app>
faces-config.xml for defining navigation-rule:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
	version="2.2">

	<navigation-rule>
		<from-view-id>registration.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>success</from-outcome>
			<to-view-id>registration_ack.xhtml</to-view-id>
		</navigation-case>

		<navigation-case>
			<from-action>#{registerBean.createNewUser}</from-action>
			<from-outcome>failure</from-outcome>
			<to-view-id>registration.xhtml</to-view-id>
		</navigation-case>

	</navigation-rule>



</faces-config>

Run the project by calling the below URL

url

registration outputregistration output1

 

Submitting the above form, throws the error “Captcha is invalid”

registration output2

Clicking on the refresh button, regenerate the captcha.

registration output3

 

The registration is successfullwhen you enter email-id as guest@abc.com and password as guest123 and the captcha as generated in the image box (here Trzngr)

registration success

 

Leave a Reply