Struts 2 login validation example using CAPTCHA


On this page, you are going to learn login validation using CAPTCHA in Struts 2 framework. CAPTCHA validation will help you to identify whether the application is used by human or robot. Creating a strong CAPTCHA will protect the application from the attacker as well as robots.

A strong CAPTCHA is a combination of uppercase, lowercase, and number and not in the form of string or character, it must be in the form of the image always because images cannot read by bots easily.

Let’s start step by step.

Technologies Used

Find the list of technology/software that I have used in this example.

  1. Eclipse Oxygen 3
  2. Tomcat 9
  3. JDK 8
  4. Struts2.3.14 JARs

Dependencies Required

To resolve the JARs dependency, you can add the following to your pom.xml file.

pom.xml
<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.3.14</version>
</dependency>

Project Structure

Have a look at project structure in Eclipse IDE.

Struts 2 login validation example using CAPTCHA

Struts 2 Filter

Add the filter class to your web.xml file.

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_2_5.xsd" id="WebApp_ID"
	version="2.5">
	<display-name>stuts2-captcha-validation</display-name>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

Action Classes

In this example, we need to create two action classes CaptchaAction.java and LoginAction.java.

CaptchaAction.java class generate the random alphanumeric character and convert it into the image and store generated CAPTCHA into the session.

CaptchaAction.java
package org.websparrow.action;

import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class CaptchaAction extends ActionSupport {

	private String date = "";
	private byte imageBytes[] = null;
	static String regex = "^[A-Za-z0-9_]+$";
	private static final Pattern check = Pattern.compile(regex);
	private String charPool[] = { "A", "B", "C", "D", "t", "u", "v", "x", "y", "z", "1", "2", "3", "4", "E", "F", "G",
			"H", "I", "J", "g", "h", "i", "j", "k", "7", "8", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
			"V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "9", "0", "5", "6", "7", "8", "9", "a", "b",
			"c", "d", "e", "f", "l", "m", "n", "o", "p", "q", "r", "s" };
	private String rndcode = "";
	private int rndNumber;
	public static final String CAPTCHA_KEY = "";
	private HttpServletResponse response;

	@Override
	public String execute() throws Exception {
		try {
			response = ServletActionContext.getResponse();
			response.setContentType("image/jpeg");
			int width = 110;
			int height = 33;
			int fontSize = 18;
			int xGap = 12;
			int yGap = 22;
			String fontName = "Arial";
			Color gradiantStartColor = Color.DARK_GRAY;
			Color gradiantEndColor = Color.DARK_GRAY;
			Color textColor = new Color(255, 255, 255);
			String[] newData = { genrateRandomCode() };
			BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D g2d = bufferedImage.createGraphics();
			RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
			g2d.setRenderingHints(rh);
			GradientPaint gp = new GradientPaint(0, 0, gradiantStartColor, 0, height / 2, gradiantEndColor, true);
			g2d.setPaint(gp);
			g2d.fillRect(0, 0, width, height);
			for (int i = 0; i < width - 10; i = i + 25) {
				int q = Math.abs(genrateSecureRandomNumber()) % width;
				int colorIndex = Math.abs(genrateSecureRandomNumber()) % 200;
				g2d.setColor(new Color(colorIndex, colorIndex, colorIndex));
				g2d.drawLine(i, q, width, height);
				g2d.drawLine(q, i, i, height);
			}
			g2d.setColor(textColor);
			int index = Math.abs(genrateSecureRandomNumber()) % newData.length;
			String captcha = newData[index];
			if (captcha != null || !captcha.isEmpty() && check.matcher(captcha).matches()) {
				ServletActionContext.getRequest().getSession().setAttribute(CAPTCHA_KEY, captcha);
			}
			int x = 0;
			int y = 0;
			for (int i = 0; i < newData[index].length(); i++) {
				Font font = new Font(fontName, Font.BOLD, fontSize);
				g2d.setFont(font);
				x += xGap + (Math.abs(genrateSecureRandomNumber()) % 7);
				y = yGap + Math.abs(genrateSecureRandomNumber()) % 12;
				g2d.drawChars(newData[index].toCharArray(), i, 1, x, y);
			}
			g2d.dispose();
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ImageIO.write(bufferedImage, "jpg", baos);
			baos.flush();
			imageBytes = baos.toByteArray();
			baos.close();
			OutputStream outputStream = response.getOutputStream();
			if (imageBytes != null) {
				outputStream.write(imageBytes);
			}
			outputStream.flush();
			outputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private String genrateRandomCode() throws NoSuchAlgorithmException {
		rndcode = "";
		for (int i = 0; i < 6; i++) {
			rndNumber = genrateSecureRandomNumber() % 50;
			rndcode = rndcode.concat(charPool[rndNumber]);
		}
		return rndcode;
	}

	public int genrateSecureRandomNumber() {
		SecureRandom secureRandomGenerator = null;
		int sr = 0;
		try {
			// For windows machine
			if (System.getProperty("os.name").startsWith("Windows")) {
				secureRandomGenerator = SecureRandom.getInstance("SHA1PRNG");
				sr = secureRandomGenerator.nextInt(1000000);
			}
			// for Linux machine
			else {
				secureRandomGenerator = SecureRandom.getInstance("NativePRNG");
				sr = secureRandomGenerator.nextInt(1000000);
			}

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return sr;
	}

	public String getDate() {
		return date;
	}

	public void setDate(String date) {
		this.date = date;
	}
}

LoginAction.java class validated the user id, password, and CAPTCHA generated by the CaptchaAction.java class and entered by the user. If all credentials are correct it will show the welcome message.

LoginAction.java
package org.websparrow.action;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {

	// Generate Getters and Setters...
	private String userId, pass, captcha, msg;

	private String sessionCaptcha = (String) ServletActionContext.getRequest().getSession()
			.getAttribute(CaptchaAction.CAPTCHA_KEY);

	@Override
	public String execute() throws Exception {

		if (sessionCaptcha.equals(captcha)) {
			if (userId.equals("admin") && pass.equals("admin")) {
				msg = "Hello, " + userId + " You have successfully logedin.";
				return SUCCESS;
			} else {
				msg = "Wrong user id or password";
				return ERROR;
			}
		} else {
			msg = "Captcha not matched.";
			return ERROR;
		}

	}
	
}

JSP Pages

Create the JSP pages for the user interface.

index.jsp
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Struts 2 login validation example using Captcha</title>
<script type="text/javascript">
	function generateNewCaptcha() {		
		document.getElementById("capImg").setAttribute("src", "");
		document.getElementById("capImg").setAttribute("src", "loadCaptcha?date="+new Date());
	}
</script>
</head>
<body>
<h1>Struts 2 login validation example using Captcha</h1>
	<pre>
		<form action="login.action" method="post">

			user:	<input type="text" name="userId" />
		
			pass:	<input type="password" name="pass" />

			captcha:<input type="text" name="captcha" />
	
			<img id="capImg" src="loadCaptcha" /> <button type="button"	onclick="generateNewCaptcha();">Refresh</button>


			<input type="submit" value="Login" />

		</form>
</pre>
	<p>	<s:property value="msg" /> </p>
</body>
</html>
welcome.jsp
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Struts 2 login validation example using Captcha</title>
</head>
<body>
	<h3><s:property value="msg" /></h3>	
</body>
</html>

Configuration

Finally, map the all-action classes into the struts.xml file.

struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
	<constant name="struts.devMode" value="true" />
	<package name="default" extends="struts-default" namespace="/">
		<action name="login" class="org.websparrow.action.LoginAction">
			<result name="success">/welcome.jsp</result>
			<result name="error">/index.jsp</result>
		</action>
		<action name="loadCaptcha" class="org.websparrow.action.CaptchaAction">
		</action>
	</package>
</struts>
Output:

Now everything is all set. Deploy the project and start the server, you will get the output as shown in the image.

Struts 2 login validation example using CAPTCHA

About the Author

Atul Rai
I like sharing my experiments and ideas with everyone by writing articles on the latest technological trends.