Integrate Spring Security in Struts 2

On the Eclipse, create a Maven project

Click Next button and select Workspace location for project

Click Next button and select maven-archetype-webapp

Click Next button and enter Project Information:

  • GroupId: LearnStrutsFrameworkWithRealApps
  • Artifact Id: LearnStrutsFrameworkWithRealApps
  • Package: com.demo

Click Finish button to finish create Maven project




Select current project, Right click and select Properties menu. In Properties dialog, select Targeted Runtime in left side after select Tomcat server from server list in right side

Click Ok button to finish

Select current project, Right click and select Build Path\Configure Build Path menu Select JRE System Library in Libraries tab:

Click Edit button and select JRE System Library you need use as below:

Click Finish button to finish




Open pom.xml file and add configurations for Struts 2 as below:

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>LearnStrutsFrameworkWithRealApps</groupId>
	<artifactId>LearnStrutsFrameworkWithRealApps</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>LearnStrutsFrameworkWithRealApps Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<!-- JSTL -->

		<dependency>
			<groupId>javax.servlet.jsp.jstl</groupId>
			<artifactId>javax.servlet.jsp.jstl-api</artifactId>
			<version>1.2.1</version>
		</dependency>

		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

		<!-- Struts 2 Framework -->

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

		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-convention-plugin</artifactId>
			<version>2.5.20</version>
		</dependency>

		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-spring-plugin</artifactId>
			<version>2.5.20</version>
		</dependency>

		<!-- Spring Framework -->

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>5.1.5.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>5.1.5.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.1.5.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>5.1.5.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>5.1.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>5.1.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>5.1.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>5.1.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>5.1.5.RELEASE</version>
		</dependency>

		<!-- Log4j -->

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.26</version>
			<scope>test</scope>
		</dependency>

	</dependencies>
	<build>
		<finalName>LearnStrutsFrameworkWithRealApps</finalName>
	</build>
</project>




Open web.xml file in src\main\webapp\WEB-INF folder and add configurations for Struts 2 as below:

<web-app 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-app_3_1.xsd"
	version="3.1">

	<display-name>Learn Struts 2 Framework with Real Apps</display-name>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>com.demo.config.AppConfig</param-value>
	</context-param>

	<context-param>
		<param-name>contextClass</param-name>
		<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
		<init-param>
			<param-name>struts.devMode</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>struts.action.extension</param-name>
			<param-value>html</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>INCLUDE</dispatcher>
	</filter-mapping>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>

</web-app>




Create new package named com.demo.config. In this package, create new java class named AppConfig.java as below:

package com.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@ComponentScan("com.demo")
@EnableTransactionManagement
@EnableWebSecurity
public class AppConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {

		httpSecurity.cors().and().csrf().disable();

		httpSecurity.authorizeRequests()
					.antMatchers("/superadmin/**").access("hasRole('ROLE_SUPER_ADMIN')")
					.antMatchers("/admin/**").access("hasRole('ROLE_SUPER_ADMIN') or hasRole('ROLE_ADMIN')")
					.antMatchers("/employee/**").access("hasRole('ROLE_SUPER_ADMIN') or hasRole('ROLE_ADMIN') or hasRole('ROLE_EMPLOYEE')")
					.and()
					.formLogin()
					.loginPage("/dashboard/login.html")
					.loginProcessingUrl("/dashboard/process-login.html")
					.defaultSuccessUrl("/dashboard/welcome.html")
					.failureUrl("/dashboard/login.html?error")
					.usernameParameter("username").passwordParameter("password")
					.and()
					.logout()
					.logoutUrl("/dashboard/logout.html")
					.logoutSuccessUrl("/dashboard/login.html?logout").and()
					.exceptionHandling()
					.accessDeniedPage("/dashboard/accessDenied.html");
	}

	@Bean
	public CorsConfigurationSource corsConfigurationSource() {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
		return source;
	}

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
			.withUser("superadmin").password(passwordEncoder().encode("123456")).authorities("ROLE_SUPER_ADMIN", "ROLE_ADMIN", "ROLE_EMPLOYEE")
			.and()
			.withUser("admin").password(passwordEncoder().encode("123456")).authorities("ROLE_ADMIN", "ROLE_EMPLOYEE")
			.and()
			.withUser("employee").password(passwordEncoder().encode("123456")).authorities("ROLE_EMPLOYEE");
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

}




Create new package named com.demo.controllers.action in src/main/java folder. The action class in Struts 2 must be put in a package named action. In this package, create new actions as below:

In com.demo.controllers.action package, create new java class named HomeAction.java as below:

package com.demo.controllers.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import com.opensymphony.xwork2.ActionSupport;

@Namespace("/home")
public class HomeAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	@Action(value = "index", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/home/index.jsp")
	})
	public String index() {
		return SUCCESS;
	}

}

In com.demo.controllers.action package, create new java class named DashBoardAction.java as below:

package com.demo.controllers.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;

import com.opensymphony.xwork2.ActionSupport;

@Namespace("/dashboard")
public class DashBoardAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	private String username;
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

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

	@Action(value = "index", results = {
		@Result(name = SUCCESS, type = "redirectAction", params = { "namespace", "/dashboard", "actionName", "login" })
	})
	public String index() {
		return SUCCESS;
	}

	@Action(value = "login", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/dashboard/login.jsp")
	})
	public String login() {
		return SUCCESS;
	}

	@Action(value = "welcome", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/dashboard/welcome.jsp")
	})
	public String welcome() {
		return SUCCESS;
	}

	@Action(value = "accessDenied", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/dashboard/accessDenied.jsp")
	})
	public String accessDenied() {
		return SUCCESS;
	}

}

In com.demo.controllers.action package, create new java class named SuperAdminAction.java as below:

package com.demo.controllers.action;

import java.util.Collection;
import java.util.Iterator;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

import com.opensymphony.xwork2.ActionSupport;

@Namespace("/superadmin")
public class SuperAdminAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	@Action(value = "index", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/superadmin/index.jsp")
	})
	public String index() {
		/* Read userDetails Info */
		UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		System.out.println("Username: " + userDetails.getUsername());
		Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) userDetails.getAuthorities();
		for (Iterator i = authorities.iterator(); i.hasNext();) {
			SimpleGrantedAuthority authority = (SimpleGrantedAuthority) i.next();
			System.out.println("Role: " + authority.getAuthority());
		}
		return SUCCESS;
	}

}




In com.demo.controllers.action package, create new java class named AdminAction.java as below:

package com.demo.controllers.action;

import java.util.Collection;
import java.util.Iterator;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

import com.opensymphony.xwork2.ActionSupport;

@Namespace("/admin")
public class AdminAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	@Action(value = "index", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/admin/index.jsp")
	})
	public String index() {
		/* Read userDetails Info */
		UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		System.out.println("Username: " + userDetails.getUsername());
		Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) userDetails.getAuthorities();
		for (Iterator i = authorities.iterator(); i.hasNext();) {
			SimpleGrantedAuthority authority = (SimpleGrantedAuthority) i.next();
			System.out.println("Role: " + authority.getAuthority());
		}
		return SUCCESS;
	}

}

In com.demo.controllers.action package, create new java class named EmployeeAction.java as below:

package com.demo.controllers.action;

import java.util.Collection;
import java.util.Iterator;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

import com.opensymphony.xwork2.ActionSupport;

@Namespace("/employee")
public class EmployeeAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	@Action(value = "index", results = {
		@Result(name = SUCCESS, location = "/WEB-INF/views/employee/index.jsp")
	})
	public String index() {
		/* Read userDetails Info */
		UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		System.out.println("Username: " + userDetails.getUsername());
		Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) userDetails.getAuthorities();
		for (Iterator i = authorities.iterator(); i.hasNext();) {
			SimpleGrantedAuthority authority = (SimpleGrantedAuthority) i.next();
			System.out.println("Role: " + authority.getAuthority());
		}
		return SUCCESS;
	}

}




Create new folder named views in src\main\webapp\WEB-INF folder. In views folder, create new views as below:

Create new folder named home. Create new jsp file named index.jsp as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<title>Home Page</title>
	</head>
	<body>

		<h3>Home Page</h3>

	</body>
</html>

Create new folder named superadmin. Create new jsp file named index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Super Admin Page</title>
</head>
<body>

	<h3>Super Admin Page</h3>
	Welcome ${pageContext.request.userPrincipal.name}
	<br>
	<s:a namespace="/dashboard" action="logout">Logout</s:a>

</body>
</html>

Create new folder named admin. Create new jsp file named index.jsp as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Admin Page</title>
</head>
<body>

	<h3>Admin Page</h3>
	Welcome ${pageContext.request.userPrincipal.name}
	<br>
	<s:a namespace="/dashboard" action="logout">Logout</s:a>

</body>
</html>

Create new folder named employee. Create new jsp file named index.jsp as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Employee Page</title>
</head>
<body>

	<h3>Employee Page</h3>
	Welcome ${pageContext.request.userPrincipal.name}
	<br>
	<s:a namespace="/dashboard" action="logout">Logout</s:a>

</body>
</html>




Create new folder named dashboard. In this folder, create new views as below:

Create new jsp file named login.jsp as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Page</title>
</head>
<body>

	<h3>Login Page</h3>
	<s:if test="%{#parameters.error != null}">
		Invalid username or password!
	</s:if>
	<s:if test="%{#parameters.logout != null}">
		Logout successfully
	</s:if>
	<s:form method="post" action="/dashboard/process-login.html">
		<s:textfield label="Username" name="username"></s:textfield>
		<s:password label="Password" name="password"></s:password>
		<s:submit value="Login" align="left"></s:submit>
	</s:form>

</body>
</html>

Create new jsp file named welcome.jsp as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome Page</title>
</head>
<body>

	<h3>Welcome Page</h3>
	Welcome ${pageContext.request.userPrincipal.name}
	<br>
	<s:a namespace="/dashboard" action="logout">Logout</s:a>

</body>
</html>

Create new jsp file named accessDenied.jsp as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Access Denied Page</title>
</head>
<body>

	<h3>Access Denied Page</h3>
	<s:a namespace="/dashboard" action="login">Login</s:a>

</body>
</html>

Create new jsp file named index.jsp in src\main\webapp folder as below:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<jsp:forward page="home/index.html"></jsp:forward>




Select project, right click and select Run on Server menu

Access index method in home action with following url:

http://localhost:8080/LearnStrutsFrameworkWithRealApps/home/index.html

Output

Access superadmin panel with url: http://localhost:8080/superadmin/index.html without login, control will redirect to login page

Output

Access admin panel with url: http://localhost:8080/admin/index.html without login, control will redirect to login page

Output

Access employee panel with url: http://localhost:8080/employee/index.html without login, control will redirect to login page

Output

To access to superadmin panel, admin panel and employee panel need login with url http://localhost:8080/LearnStrutsFrameworkWithRealApps/dashboard/login.html

Output

Test login with invalid account: username is abc and password is 123

Output

Login superadmin panel with valid account: username is superadmin and password is 123456

Output

After login with SuperAdmin Role we can test access to superadmin panel, admin panel and employee panel with urls as below:

  • SuperAdmin Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/superadmin/index.html
  • Admin Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/admin/index.html
  • Employee Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/employee/index.html

Output 1

Output 2

Output 3

Login admin panel with valid account: username is admin and password is 123456

Output

After login with Admin Role we can test access to superadmin panel, admin panel and employee panel with urls as below:

  • SuperAdmin Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/superadmin/index.html
  • Admin Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/admin/index.html
  • Employee Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/employee/index.html

Output 1

Output 2

Output 3

Login employee panel with valid account: username is employee and password is 123456

Output

After login with Employee Role we can test access to superadmin panel, admin panel and employee panel with urls as below:

  • SuperAdmin Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/superadmin/index.html
  • Admin Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/admin/index.html
  • Employee Panel: http://localhost:8080/LearnStrutsFrameworkWithRealApps/employee/index.html

Output 1

Output 2

Output 3