Struts 2 Interceptor Tutorial with Custom Authentication Interceptor Example

While working on Struts 2, most of the time you will spend on Action Classes but Interceptors are the backbone of Struts 2 Framework and we should know about them. Struts 2 interceptors are responsible for most of the processing done by the framework, such as passing request params to action classes, making Servlet API request, response, session available to Action classes, validation, i18n support, etc.

Struts 2 Framework provides a bunch of interceptors and most of them are defined in struts-default package and used in defaultStack interceptor stack. Interceptors are the power of Struts 2 framework that plays a crucial role in achieving high level of separation of concerns.

In our Struts 2 Beginners Tutorial, we looked at the Struts 2 architecture and saw that every request and response passes through interceptors allowing them to inject different objects, perform specific operations on request and response and do some cross cutting tasks for the application.

ActionInvocation is responsible to incapsulate Action classes and interceptors and to fire them in order. The most important method for use in ActionInvocation is invoke() method that keeps track of the interceptor chain and invokes the next interceptor or action. This is one of the best example of Chain of Responsibility pattern in Java EE frameworks.

Struts 2 API reads the interceptors defined for an action in the order it’s declared. So if interceptor1 and interceptor2 are defined for action1 then ActionInvocation will invoke them in following chain.

interceptor1 -> interceptor2 -> action1 -> interceptor2 -> interceptor1

If any of the interceptor intercept() method decides that action should not get executed, it can return a string value and response will be created from global result with that name from struts configuration file.

Interceptors and Global Results configuration

We can define global results in struts.xml file as:

		<global-results>
			<result name="login" type="redirect">/login.action</result>
		</global-results>

These results can be used by any of the action classes.

Defining interceptors for a package includes several steps and we need to define them with interceptors, interceptor, interceptor-stack and default-interceptor-ref elements. They should be the first elements of the package or else you will get error message as

The content of element type “package” must match “(result-types?,interceptors?,default-
interceptor-ref?,default-action-ref?,default-class-ref?,global-results?,global-exception-
mappings?,action*)”.

A simple example of interceptor configuration is:

	<package name="user" namespace="/" extends="struts-default">
		<interceptors>
			<interceptor name="authentication"
				class="com.journaldev.struts2.interceptors.AuthenticationInterceptor"></interceptor>
			<interceptor-stack name="authStack">
				<interceptor-ref name="authentication"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>
		</interceptors>

		<default-interceptor-ref name="authStack"></default-interceptor-ref>
	</package>

Note that defaultStack is already configured in struts-default package, that’s why we don’t need to define that in above example.

Let’s create a simple application where we will use custom interceptor class to provide authentication for our application. Using interceptor will make our application loosely coupled, flexible and configurable. Our final project will look like below image.

Struts2-interceptor-example-project

Project configuration Files

<?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>Struts2InterceptorExample</display-name>

	<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>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>Struts2InterceptorExample</groupId>
	<artifactId>Struts2InterceptorExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-core</artifactId>
			<version>2.3.15.1</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

pom.xml and web.xml configuration files are self understood.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
	<constant name="struts.convention.result.path" value="/"></constant>

	<package name="user" namespace="/" extends="struts-default">
		<interceptors>
			<interceptor name="authentication"
				class="com.journaldev.struts2.interceptors.AuthenticationInterceptor"></interceptor>
			<interceptor-stack name="authStack">
				<interceptor-ref name="authentication"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>
		</interceptors>

		<default-interceptor-ref name="authStack"></default-interceptor-ref>

		<global-results>
			<result name="login" type="redirect">/home.action</result>
		</global-results>

		<action name="home">
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<result>/login.jsp</result>
		</action>

		<action name="login" class="com.journaldev.struts2.actions.LoginAction">
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<result name="success">/welcome.jsp</result>
			<result name="input">/login.jsp</result>
		</action>
		<action name="welcome" class="com.journaldev.struts2.actions.WelcomeAction">
			<result name="success">/welcome.jsp</result>
		</action>

	</package>

</struts>

Notice that I am declaring an interceptor authentication which is referring to class com.journaldev.struts2.interceptors.AuthenticationInterceptor. Also notice that authStack is the default interceptor-stack for the package. I am not using this stack for login.jsp because it’s used for login and at that time user will not have session to authenticate.

Result Pages

<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%-- Using Struts2 Tags in JSP --%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Login Page</title>
</head>
<body>
<h3>Welcome User, please login below</h3>
<s:form action="login">
	<s:textfield name="user" label="User Name"></s:textfield>
	<s:textfield name="password" label="Password" type="password"></s:textfield>
	<s:submit value="Login"></s:submit>
</s:form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Welcome Page</title>
</head>
<body>
<h3>Welcome <s:property value="userName"></s:property></h3>
</body>
</html>

JSP result pages are easy to understand and nothing related to interceptors.

Model Class

package com.journaldev.struts2.models;

public class User {

	private String user;
	private String password;
	private String userName;
	public String getUser() {
		return user;
	}
	public void setUser(String user) {
		this.user = user;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	
}

A simple java bean with properties that are used in result pages. We will use this java bean in action class to hold properties.

Custom Interceptor

package com.journaldev.struts2.interceptors;

import com.journaldev.struts2.models.User;


public interface UserAware {

	public void setUser(User user);
}

An interface similar to Struts 2 API *Aware interface, we will use it in our custom interceptor to inject values in action class. To learn more about Struts 2 API *Aware interface, please read:

How to get Servlet Session, Request, Response, Context Attributes in Struts 2 Action

package com.journaldev.struts2.interceptors;

import java.util.Map;

import com.journaldev.struts2.models.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class AuthenticationInterceptor implements Interceptor {

	private static final long serialVersionUID = -5011962009065225959L;

	@Override
	public void destroy() {
		//release resources here
	}

	@Override
	public void init() {
		// create resources here
	}

	@Override
	public String intercept(ActionInvocation actionInvocation)
			throws Exception {
		System.out.println("inside auth interceptor");
		Map<String, Object> sessionAttributes = actionInvocation.getInvocationContext().getSession();
		
		User user = (User) sessionAttributes.get("USER");
		
		if(user == null){
			return Action.LOGIN;
		}else{
			Action action = (Action) actionInvocation.getAction();
			if(action instanceof UserAware){
				((UserAware) action).setUser(user);
			}
			return actionInvocation.invoke();
		}
	}

}

This is our custom interceptor class that is implementing com.opensymphony.xwork2.interceptor.Interceptor interface. init() and destroy() are interceptor lifecycle methods that we can implement to initialize and destroy some resources used in interceptor intercept() method.

intercept() is the method invoked by ActionInvocation class, so this is where our interceptor code goes. We can get session attributes from ActionInvocation reference and use it to make sure session is valid. If we don’t find the attribute, we are returning global result “login” that will redirect user to login page. If session is valid, then we are injecting user into action class. instanceof keyword is used to make sure that action class is implementing UserAware interface. Finally we are calling ActionInvocation invoke() method that will call the next interceptor or action class in the chain.

Action Classes

package com.journaldev.struts2.actions;

import java.util.Map;

import org.apache.struts2.interceptor.SessionAware;

import com.journaldev.struts2.models.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class LoginAction extends ActionSupport implements SessionAware, ModelDriven<User>{

	private static final long serialVersionUID = -3369875299120377549L;

	@Override
	public String execute(){
		System.out.println("inside execute");
		if("pankaj".equals(user.getUser()) && "admin".equals(user.getPassword())){
			user.setUserName("Pankaj Kumar");
			sessionAttributes.put("USER", user);
			return SUCCESS;
		}
		return INPUT;
	}
	
	private User user = new User();
	private Map<String, Object> sessionAttributes = null;

	@Override
	public void setSession(Map<String, Object> sessionAttributes) {
		this.sessionAttributes = sessionAttributes;
	}
	
	@Override
	public User getModel() {
		return user;
	}
	
}

Notice that LoginAction is implementing SessionAware interface to get session attributes and if user is validated, then putting user attribute in session to be used in authentication interceptor for validation. Notice that LoginAction is not implementing UserAware interface.

package com.journaldev.struts2.actions;

import com.journaldev.struts2.interceptors.UserAware;
import com.journaldev.struts2.models.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class WelcomeAction extends ActionSupport implements UserAware, ModelDriven<User> {

	private static final long serialVersionUID = 8111120314704779336L;

	@Override
	public String execute(){
		return SUCCESS;
	}
	
	private User user;
	@Override
	public void setUser(User user) {
		this.user=user;
	}
	
	public User getUser(User user){
		return this.user;
	}

	@Override
	public User getModel() {
		return this.user;
	}

}

Notice that WelcomeAction is implementing UserAware interface and our AuthenticationInterceptor is injecting it to the action class behind the scene. Notice that action class is simple and there is no code for validating the user session or to set the user bean.

From above action classes code, it’s clear that if we use interceptors smartly, we can reduce a lot of code redundancy for common tasks that we need to do for multiple actions.

When we execute above project, we get following response pages.

Struts2-interceptor-example-login

Struts2-interceptor-example-success

If you are not logged in and you will try to invoke login.action, our authentication interceptor will forward you to login page and action class will never be invoked.

That’s all for Struts 2 interceptors tutorial, it’s one of the important features of Struts 2 and you should try to use it for reusability.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current ye@r *

Subscribe to JournalDev Newsletter
Get the FREE access to Monthly Newsletter and Free PDF eBooks
*No Spam Guaranteed. By entering your email address, you agree also subscribing to our newsletter.
Oops! - Something went wrong.
Close
"Oracle Magazine for Developers" PDF eBook Free Download Now
Exclusive Offer: Citrix™ Whitepaper on Denial of Service Attack Download Now