MVC

Spring MVC Application with Spring Security Example

In one of our past examples, we learned to create a simple Spring MVC web-applciation. In this example we will demonstrate how we can implement Spring-Security to secure our web-application. We shall discuss and demonstrate both Authentication as well as the Authorization aspect of an application’s security.

1. Introduction to Spring Security

Security of a web-application revolves around three major concepts :

  • Authentication
  • Authorization
  • Encryption

 
First let’s understand What is Authentication and Authorization?

  • Authentication is the process of determining if the user is, who he claims to be. If the user enters his username as XYZ, then he should be able to prove that he is XYZ by providing the password known only to user XYZ.
  • Authorization is usually the next step after authentication wherein the system determines if the authenticated user is privileged to access the resource requested.

We shall leave out Encryption as it is beyond the scope of this write-up.

Spring Security provides authentication and authorization in a very flexible manner and is also easy to configure and interpret. Let’s start with project setup.

2. Project Setup

We shall use Maven to setup our project. Open Eclipse and create a simple Maven project and check the skip archetype selection checkbox on the dialogue box that appears. Replace the content of the existing pom.xml with the one provided below:

pom.xml

<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>com.javacodegeeks.examples</groupId>
	<artifactId>SpringWebwithSpringSecurity</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringWebwithSpringSecurity Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.2.3.RELEASE</version>
		</dependency>

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


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

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

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

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

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



		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.2.3.RELEASE</version>
		</dependency>

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

	</dependencies>
	<build>
		<finalName>SpringWebwithSpringSecurity</finalName>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

</project>

This will import the required JAR dependencies in the project. We can now start with the actual Spring-Security implementation.

3. Project Implementation

Fig 1 : Project Structure
Fig 1 : Project Structure

Let’s start with the gateway of the J2EE web-application, the WEB.xml. We need to declare the SpringSecurityFilterChain.

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

	<display-name>Servlet 3.0 Web Application</display-name>
	<display-name>Spring Security Example</display-name>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:/security-config.xml
		</param-value>
	</context-param>

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

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

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

	<servlet>
		<servlet-name>Spring-Controller</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:/springWeb.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Spring-Controller</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

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

</web-app>

Spring Security intercepts the incoming request via a Servlet filter – springSecurityFilterChain. The DelegatingFilterProxy is a proxy for actual spring bean object which implements the javax.servlet.Filter interface. This filter guards the web-application from a host of malicious attacks like CSRF , Session Fixation, XSS etc.

We pass the location of spring security config file – security-config.xml to the filter via the contextConfigLocation web context parameter. Let’s have a look at security-config.xml:

security-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-4.0.xsd
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
      http://www.springframework.org/schema/context
	  http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<sec:http auto-config="true" use-expressions="true">
		<sec:form-login login-page="/login"
			login-processing-url="/authenticateUser" default-target-url="/welcome"
			authentication-failure-url="/login" username-parameter="username"
			password-parameter="password" />
			
		<sec:access-denied-handler error-page="/403.jsp" />
		
		<sec:intercept-url pattern="/login" access="permitAll" />
		
		<sec:intercept-url pattern="/**" access="hasAuthority('AUTH_USER')" />
		<sec:session-management invalid-session-url="/login" />
		<sec:logout delete-cookies="JSESSIONID" logout-url="/logout" />
	</sec:http>

	<context:component-scan base-package="com.jcg.examples" />

	<sec:authentication-manager>
		<authentication-provider ref="customAuthenticationProvider" />
	</sec:authentication-manager>

</beans:beans>

This is the file where we configure the actual security parameters for our application. The acts as a container for all HTTP-related security settings.

sec:form-login is the login form shown to the user when he tries to access any resource in the web-application. If we do not provide a login form, the spring provides its default login page with a username, password fields and submit button. The username-parameter and password-parameter are the names of the username and the password fields that the login page has. When these attributes are not explicitly provided they default to j_username and j_password. It is wise to rename to hide underlying technology. Spring extracts the username and password from the request using the names provided and provides them in the org.springframework.security.core.Authentication object.

The login-processing-url is the actual url which holds the resource to authenticate the user. We have defined a custom authenticator class and mapped it to /authenticateUser URL. We will look in to this class in detail in the next section.

The developer may define multiple sec:intercept-url. This specifies the roles authorized to access the resource mapped by this filter-pattern. The user may also use hasRole expression to authenticate based in the user-roles, but in that case the role-name must start with ROLE_ or else the user is denied the access. The user may also choose to waive authentication process for certain resources from all security check like the login page, Javascript and CSS files. Not doing so may lead to infinite redirects to the same login page.

sec:logout tag is used to customize the logging-out process from the web-application.

The sec:authentication-manager is the actual bean that authenticates the user based on the username and password he enters in the login page. Here’s how the custom authenticator class looks like:

CustomAuthenticationProvider.java

package com.jcg.examples.authentication;


import java.util.ArrayList;
import java.util.List;

import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;


@Component
public class CustomAuthenticationProvider implements AuthenticationProvider
{
		@Override
		public Authentication authenticate(Authentication authentication) throws AuthenticationException
		{
				String userName = authentication.getName();
				String password = authentication.getCredentials().toString();

				if (authorizedUser(userName, password))
				{
						List<GrantedAuthority> grantedAuths = new ArrayList<>();
						grantedAuths.add(()-> {return "AUTH_USER";});
						Authentication auth = new UsernamePasswordAuthenticationToken(userName, password, grantedAuths);
						System.out.println(auth.getAuthorities());
						return auth;
				}
				else
				{
						throw new AuthenticationCredentialsNotFoundException("Invalid Credentials!");
				}
		}

		private boolean authorizedUser(String userName, String password)
		{
				System.out.println("username is :" + userName+" and password is "+password );
				if("Chandan".equals(userName) && "Chandan".equals(password))
						return true;
				return false;
		}

		@Override
		public boolean supports(Class<?> authentication)
		{
				return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
		}
}

Our custom authenticator class implements the org.springframework.security.authentication.AuthenticationProvider interface. The interface provides us with simple method which will help simplify the process of user-authentication for us.

authenticate(Authentication authentication) : This method takes the authentication request object as a parameter. This object contains the username and password the user entered in the login page. Upon successful authentication the users roles are populated in a new org.springframework.security.authentication.UsernamePasswordAuthenticationToken authentication object. The resource requested by the user is then checked against the role in this authentication object. If the role matches the access rights for the user is allowed to access the resource. If not, the user is redirected to the error-page defined in the sec:access-denied-handler tag.

In this example we have implemented the org.springframework.security.core.GrantedAuthority interface using the lambda expression and provided the user with the AUTH_USER role.

Once the user is successfully authenticated and authorized, the url is directed to DispatcherServlet configured in the web.xml. The DispatcherServlet in turn invokes the Controller method mapped to the resource’s url.

Here’s a simple configuration xml for the initialization of the controllers. Remember to pass this xml file-name in the init-param of the DispatcherServlet in web.xml.

springWeb.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="com.jcg.examples" />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/jsp/" />
      <property name="suffix" value=".jsp" />
   </bean>
   
</beans>

We have also configured the JSP view-resolver for the view resolution. Let’s see have a look at the JSP files:

login.jsp

 

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login</title>
</head>
<body>
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
      <font color="red">
        Your login attempt was not successful due to <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.
      </font>
</c:if>
	<form name="loginForm" action="authenticateUser" method="post">
		User-name<input type="text" name="username" /><br /> Password <input
			type="password" name="password" /> <input type="hidden"
			name="${_csrf.parameterName}" value="${_csrf.token}" /> <input
			type="submit" value="Submit">
	</form>
</body>
</html>

As I have already explained, the names of the username and the password have been configured in the sec:form-login tag of the security-config.xml as is the authentication URL. There is also a hidden field which stores a random token to be submitted with the request. This helps to guard against the CSRF attack.

Here’s the login.jsp in browser:

Fig 2 : Login Page
Fig 2 : Login Page

Upon successful authentication, the user is rendered the welcome.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!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=ISO-8859-1">
<title>WELCOME</title>
</head>
<body>
Welcome! Your login was successful...!

<a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

Here’s how it looks:

Fig 3 : Welcome Page
Fig 3 : Welcome Page

In case the user enters wrong password he is redirected back to the login page with the message that is thrown from the CustomAuthenticationProvider#authenticate method. We can access the message using the SPRING_SECURITY_LAST_EXCEPTION variable in the login.jsp.

If the user is authenticated but his role does not allow him to access the resource, he is re-directed to the Access Denied page as shown here:

Fig 4 : Access Denied
Fig 4 : Access Denied

4. Download the Source Code

Here, we studied how we can user spring-security to enable access control in our web-application.

Download
You can download the source code of this example here: SpringWebwithSpringSecurity.zip

Chandan Singh

Chandan holds a degree in Computer Engineering and is a passionate software programmer. He has good experience in Java/J2EE Web-Application development for Banking and E-Commerce Domains.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button