Spring Security Form based Authentication – XML Configuration

In this tutorial I will show you the way to configure Spring Security with Spring MVC web application to secure pages. I will create spring mvc based web application and I will configure Spring Security to protect a page from outside access.

You may also like annotation based example Spring Security Form based Authentication – Annotations

Spring Security allows to you to integrate security features with JEE web application easily, it takes care about all incoming HTTP requests via servlet filter, and implements “user defined” security checking.

In this tutorial, I will show you how to integrate Spring Security 4.2.1 with Spring MVC4 web application to secure URL access.
For this tutorial I will create maven based web project in Eclipse.

Prerequisites
The following configurations are required in order to run the application
Eclipse Kepler
JDK 1.8
Tomcat 8
Have maven 3 installed and configured
Spring mvc 4 dependencies in pom.xml
Spring security 4 dependencies in pom.xml
Now we will see the below steps how to create a maven based project in Eclipse

Step 1. Create a maven based web project in Eclipse

Go to File -> New -> Other. On popup window under Maven select Maven Project. Then click on Next. Select the workspace location – either default or browse the location. Click on Next. Now in next window select the row as highlighted from the below list of archtypes and click on Next button.

maven-arctype-webapp
Now enter the required fields (Group Id, Artifact Id) as shown below
Group Id : com.roytuts
Artifact Id : spring-security
Step 2. Modify the pom.xml file as shown below.

The minimal dependency artifacts required for Spring Security are spring-security-web and spring-security-config.

<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.roytuts</groupId>
	<artifactId>spring-security</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-security Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<properties>
		<java.version>1.8</java.version>
		<spring.version>4.3.4.RELEASE</spring.version>
		<spring.security.version>4.2.1.RELEASE</spring.security.version>
	</properties>
	<dependencies>
		<!-- The spring-webmvc module contains Spring’s model-view-controller (MVC)
			and REST Web Services implementation for web applications -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<!-- Spring Security -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${spring.security.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${spring.security.version}</version>
		</dependency>
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
			<scope>provided</scope>
		</dependency>
		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>spring-security</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Step 3. If you see JRE System Library[J2SE-1.5] then change the version by below process

Do right-click on the project and go to Build -> Configure build path, under Libraries tab click on JRE System Library[J2SE-1.5], click on Edit button and select the appropriate jdk 1.8 from the next window. Click on Finish then Ok.

Step 4. Now when the build process finished then modify the web.xml file with below source code

<?xml version="1.0" encoding="UTF-8"?>
<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>Archetype Created Web Application</display-name>
	<!-- Spring security -->
	<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>
	<!-- dispatcher servlet acts as a front controller for each request/response -->
	<servlet>
		<servlet-name>SpringController</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- load Spring controllers while dispatcher servlet loads -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:controllers.xml, classpath:security.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringController</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

In web.xml, the first thing is to add the filter declaration DelegatingFilterProxy to your web.xml file as shown above.

This provides a hook into the Spring Security web infrastructure. DelegatingFilterProxy is a Spring Framework class which delegates to a filter implementation which is defined as a Spring bean in your application context. In this case, the bean is named springSecurityFilterChain, which is an internal infrastructure bean created by the namespace to handle web security. Note that you should not use this bean name yourself. Once you’ve added this to your web.xml, you’re ready to start editing your application context file. Web security services are configured using the <http> element.

I have also declared Spring MVC DispatcherServlet, that acts as a front controller, as shown above in the web.xml file to handle incoming request and response for the URL pattern "/".

Step 5. Create security.xml file under src/main/resources directory with the below source code

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:security="http://www.springframework.org/schema/security"
	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.xsd
		http://www.springframework.org/schema/security
		http://www.springframework.org/schema/security/spring-security.xsd">
	<security:http auto-config="true" use-expressions="true">
		<security:intercept-url pattern="/admin"
			access="hasRole('ROLE_ADMIN')" />
		<security:form-login login-page="/login"
			login-processing-url="/j_spring_security_check" default-target-url="/admin"
			authentication-failure-url="/login?error" username-parameter="username"
			password-parameter="password" />
	</security:http>
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="roy" password="roy" authorities="ROLE_ADMIN" />
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
</beans>

Security Namespace Configuration

A namespace element can be used simply to allow a more concise way of configuring an individual bean or, more powerfully, to define an alternative configuration syntax which more closely matches the problem domain and hides the underlying complexity from the user. A simple element may conceal the fact that multiple beans and processing steps are being added to the application context.

Design of the Namespace – xmlns:security

Web/HTTP Security – the most complex part. Sets up the filters and related service beans used to apply the framework authentication mechanisms, to secure URLs, render login and error pages and much more.
Business Object (Method) Security – options for securing the service layer.
AuthenticationManager – handles authentication requests from other parts of the framework.
AccessDecisionManager – provides access decisions for web and method security. A default one will be registered, but you can also choose to use a custom one, declared using normal Spring bean syntax.
AuthenticationProviders – mechanisms against which the authentication manager authenticates users. The namespace provides supports for several standard options and also a means of adding custom beans declared using a traditional syntax.
UserDetailsService – closely related to authentication providers, but often also required by other beans.

<security:http/> enables the web security. <security:http> element is the parent for all web-related namespace functionality. The <security:intercept-url> element defines a pattern which is matched against the URLs of incoming requests using an ant path style syntax. The access attribute defines the access requirements for requests matching the given pattern.

In the above configuration we want URL that has pattern /admin within our application to be secured, requiring the role ROLE_ADMIN to access them, we want to log in to the application using a custom login form with username and password, and that we want a logout URL registered which will allow us to log out of the application.

If you do not want to use custom login form then you can simply use <security:form-login/> to get the Spring’s default login form.

To add a user, for example in our case the username is roy, you need to use <security:authentication-manager/>. The <security:authentication-provider> element creates a DaoAuthenticationProvider bean and the <security:user-service> element creates an InMemoryDaoImpl. All authentication-provider elements must be children of the <security:authentication-manager> element, which creates a ProviderManager and registers the authentication providers with it. The configuration above defines one user, its password and its role within the application (which will be used for access control). It is also possible to load user information from a standard properties file using the properties attribute on user-service.

Step 6. Create controllers.xml file under src/main/resources directory with the below source code

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
	<mvc:annotation-driven />
	<mvc:resources location="/static/" mapping="/static/**" />
	<context:component-scan base-package="com.roytuts.spring.security.controllers" />
	<bean id="jspViewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<bean id="messageSource"
		class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basename" value="messages" />
	</bean>
</beans>

I have configured <mvc:annotation-driven /> to work with annotations in Spring MVC.

I have also configured <mvc:resources location="/static/" mapping="/static/**" /> to load static resources from static directory.

I have configured <context:component-scan/> to load all annotation-driven controllers from the given base package.

I have also declared view resolver bean and message resource for i18n supports.
Step 7. Create below messages.properties file with below content and put it under src/main/resources folder

page.title=Spring Security Basic (XML)
page.home.heading=Home Page
page.login.heading=Login Here
page.admin.heading=Administrator Control Panel
page.admin.message=This page demonstrates how to use Spring security.
page.goto.admin=Go to Administrator page
login.failure.reason=Invalid credentials
welcome.msg=Welcome
logout.text=Logout
logout.msg.success=You have been successfully logged out.

Step 8. Create below controller with below source

package com.roytuts.spring.security.controllers;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Component
@Controller
public class SpringSecurityController implements MessageSourceAware {
	private MessageSource messageSource;
	@Override
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}
	@RequestMapping("/")
	public String defaultPage() {
		return "index";
	}
	@RequestMapping("/login")
	public String loginPage(Model model, @RequestParam(value = "error", required = false) String error,
			@RequestParam(value = "logout", required = false) String logout) {
		if (error != null) {
			model.addAttribute("error", messageSource.getMessage("login.failure.reason", null, Locale.US));
		}
		if (logout != null) {
			model.addAttribute("msg", messageSource.getMessage("logout.msg.success", null, Locale.US));
		}
		return "login";
	}
	@RequestMapping("/logout")
	public String logoutPage(Model model, HttpServletRequest request) {
		request.getSession().invalidate();
		return "redirect:/login?logout";
	}
	@RequestMapping("/admin")
	public String adminPage(Model model) {
		model.addAttribute("title", messageSource.getMessage("page.admin.heading", null, Locale.US));
		model.addAttribute("message", messageSource.getMessage("page.admin.message", null, Locale.US));
		return "admin";
	}
}

Step 9. We need some style, so create the below style.css file and put it under webapp/static/css directory

.error {
	padding: 15px;
	margin-bottom: 20px;
	border: 1px solid transparent;
	border-radius: 4px;
	color: #a94442;
	background-color: #f2dede;
	border-color: #ebccd1;
}
.msg {
	padding: 15px;
	margin-bottom: 20px;
	border: 1px solid transparent;
	border-radius: 4px;
	color: #31708f;
	background-color: #d9edf7;
	border-color: #bce8f1;
}
#login-box {
	width: 500px;
	padding: 20px;
	margin: 50px auto;
	background: #fff;
	-webkit-border-radius: 2px;
	-moz-border-radius: 2px;
	border: 1px solid #000;
}

Step 10. Below is the index.jsp file and put it under webapp directory and see how keys are used to fetch corresponding value from messages.properties file. This index.jsp file is not secured and is accessible directly.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title><fmt:bundle basename="messages">
		<fmt:message key="page.title" />
	</fmt:bundle></title>
</head>
<body>
	<div align="center">
		<h1>
			<fmt:bundle basename="messages">
				<fmt:message key="page.home.heading" />
			</fmt:bundle>
		</h1>
		<a href="${pageContext.request.contextPath}/admin"><fmt:bundle
				basename="messages">
				<fmt:message key="page.goto.admin" />
			</fmt:bundle></a>
	</div>
</body>
</html>

Step 11. Below admin.jsp file in webapp directory is secured and user must login before viewing the content of this file. When you try to access the admin.jsp file then you will automatically be redirected to the login.jsp file.

<%@ page language="java" session="true"
	contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title><fmt:bundle basename="messages">
		<fmt:message key="page.title" />
	</fmt:bundle></title>
</head>
<body>
	<div align="center">
		<h1>${title}</h1>
		<h2>${message}</h2>
		<c:if test="${pageContext.request.userPrincipal.name != null}">
			<h2>
				<fmt:bundle basename="messages">
					<fmt:message key="welcome.msg" />
				</fmt:bundle>
				: ${pageContext.request.userPrincipal.name} | <a
					href="<c:url value='logout'/>"><fmt:bundle basename="messages">
						<fmt:message key="logout.text" />
					</fmt:bundle></a>
			</h2>
		</c:if>
	</div>
</body>
</html>

Step 12. The content of the login.jsp file under webapp directory.

<%@ page language="java" session="true"
	contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt"%>
<html>
<head>
<title><fmt:bundle basename="messages">
		<fmt:message key="page.title" />
	</fmt:bundle></title>
<link rel="stylesheet" type="text/css"
	href="<c:url value="/static/css/style.css"/>" />
</head>
<body>
	<div id="login-box">
		<h2>
			<fmt:bundle basename="messages">
				<fmt:message key="page.login.heading" />
			</fmt:bundle>
		</h2>
		<c:if test="${not empty error}">
			<div class="error">${error}</div>
		</c:if>
		<c:if test="${not empty msg}">
			<div class="msg">${msg}</div>
		</c:if>
		<form name='loginForm'
			action="<c:url value='j_spring_security_check' />" method='POST'>
			<table>
				<tr>
					<td>User:</td>
					<td><input type='text' name='username' value=''></td>
				</tr>
				<tr>
					<td>Password:</td>
					<td><input type='password' name='password' /></td>
				</tr>
				<tr>
					<td colspan='2'><input name="submit" type="submit"
						value="Submit" /></td>
				</tr>
			</table>
			<input type="hidden" name="${_csrf.parameterName}"
				value="${_csrf.token}" />
		</form>
	</div>
</body>
</html>

Step 13. When you deploy the application and run the application you will see different output in the browser.
When you hit the URL http://localhost:8080/spring-security/

spring security

When you click on link Go to Administrator page

spring security

When you click on Submit button without giving any credentials or wrong credentials

spring security

When you give username/password as roy/roy

spring security

When you click on Logout link

spring security

Thanks for reading.

Leave a Reply

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