Spring Security: Customize Login Form

In this article, we will extend the same example implemented in the previous article thus customizing the login-form overriding the default login-form provided by Spring framework. This way developer/designer can add more appealing look to the login page

In this example,

  • /admin* will be intercepted by the security filters and we need to KEY-IN the correct credentials (Username/Password) to access the admin page
  • other like /home* won’t be intercepted by security filters

Technology Used

  • Java 1.7
  • Eclipse Luna IDE
  • Spring-4.0.0-RELEASE
  • Apache-Maven-3.2.1
  • Apache Tomcat 7.0.54

Mavenize or download required jars

Add Spring-4.0.0 and Spring-security-3.2.0 dependencies to the pom.xml

<dependencies>
		<!-- junit -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<!-- jstl for jsp page -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>${jstl.version}</version>
		</dependency>

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

		<!-- Spring 4.0.0.RELEASE Framework -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring Security 3.2.0.RELEASE Framework -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${spring.security.version}</version>
		</dependency>
		<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>
	</dependencies>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the spring site and include them in the classpath

  • spring-core-4.0.0-RELEASE
  • spring-context-4.0.0-RELEASE
  • spring-beans-4.0.0-RELEASE
  • spring-aop-4.0.0-RELEASE
  • spring-expression-4.0.0-RELEASE
  • spring-web-4.0.0-RELEASE
  • spring-webmvc-4.0.0-RELEASE
  • spring-security-core-3.2.0-RELEASE
  • spring-security-web-3.2.0-RELEASE
  • spring-security-config-3.2.0-RELEASE
  • commons-logging-1.1.1
  • aopalliance-1.0
  • junit-3.8.1
  • jstl-1.2

Directory Structure

Before moving on, let us understand the directory/package structure once you create project in Eclipse IDE
Maven has to follow certain directory structure

  • src/test/java –> test related files, mostly JUnit test cases
  • src/main/java –> create java source files under this folder
  • src/main/resources –> all configuration files placed here
  • Maven Dependencies or Referenced Libraries –> includes jars in the classpath
  • WEB-INF under webapp –> stores web.xml & other configuration files related to web application

Project Structure (Package Explorer view in Eclipse)

1_SpringSecurity_CustomLogin_Basic_Project_Structure_In_Eclipse

Jars Libraries Used in the Project (Maven Dependencies)

2_SpringSecurity_CustomLogin_Jars_In_Classpath

Web application

For any web application, entry point is web.xml which describes how the incoming http requests are served / processed. Further, it describes about the global-context and local-context param (i.e.; <context-param> & <init-param>) for loading the files particular to project requirements & contains respective listener

With this introduction, we will understand how we configured web.xml for our Spring Security + Spring MVC application

web.xml (the entry point à under WEB-INF)

This web.xml file describes,

  • all http requests with wild card pattern “/” will be intercepted by the configured servlet called “DispatcherServlet” (org.springframework.web.servlet.DispatcherServlet)
  • </servlet-name> name mentioned in the web.xml is mvc-dispatcher, which on loading/exploding the war into the application server(Tomcat application server for our example) looks for the servlet filename called “mvc-dispatcher-servlet.xml”
  • <context-param> with its attributes describes the location of the file from where it has to be loaded
  • “mvc-dispatcher-servlet.xml” is the file which describes how exactly specific http requests are handled or which controllers gets invoked for certain http requests. We will see “mvc-dispatcher-servlet.xml” file for our Spring MVC + Spring Security application
  • <welcome-file-list> files under this tag is the start-up page
  • Servlet filter called “DelegatingFilterProxy” with url-pattern /* intercepts incoming http requests and enter Spring Security framework for security checks to authenticate users
  • “spring-security.xml” describes which URL needs to be intercepted along with their roles/credentials

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" version="2.5">

	<!-- project display for web application -->
	<display-name>SpringSecurity</display-name>

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

	<!-- Spring MVC DispatcherServlet: dispatches HTTP requests to registered controllers -->
	<servlet>
		<servlet-name>mvc-dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>mvc-dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<!-- location of the root application context xml file -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/mvc-dispatcher-servlet.xml,
            /WEB-INF/spring-security.xml
        </param-value>
	</context-param>

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

	<!-- 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>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>INCLUDE</dispatcher>
		<dispatcher>ERROR</dispatcher>
	</filter-mapping>
</web-app>

Dispatcher Servlet

Below mvc-dispatcher-servlet.xml file defines,

  • <context:component-scan base-package=”” /> this tag scans all classes & sub-classes under the value of base-package attribute and register them with the Spring container
  • other bean element  for view resolver defines the logic for output rendering i.e.; viewName returned by the Controller which will be sandwiched between the prefix (WEB-INF/jsp) & suffix (.jsp)
  • Now, there should be a file under the directory as defined i.e.; (WEB-INF/jsp/<viewName>.jsp
  • otherwise, not found exception thrown.

mvc-dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- register beans for handling incoming HTTP requests -->
	 <context:component-scan base-package="com.spring.series.security.controller" />  

	<!-- view resolver for rendering the final output -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix">
			<value>/WEB-INF/jsp/</value>
		</property>
		<property name="suffix">
			<value>.jsp</value>
		</property>
	</bean>
</beans>

Spring Security Configuration

This Spring Security configuration file describes the security URL to be intercepted and login details for that particular role

  • First element <security:http /> with pattern describes which all incoming http requests needs to be intercepted
  • Second element with <security:authentication-manager /> defines the credentials (Name/Password) for every role. For example, ROLE_ADMIN, ROLE_USER, etc.

login-page: this is mapped to custom login page we created for this example
default-target-url: on successful login, this page will be displayed to the user
authentication-failure-url: page will be redirected to this URL for invalid credentials
logout-success-url: on successful logout, page will be redirected to this URL

spring-security.xml

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

	<security:http auto-config="true">
		<security:intercept-url pattern="/admin*" access="ROLE_ADMIN" />
		<security:form-login login-page="/login" default-target-url="/admin" authentication-failure-url="/error" />
		<security:logout logout-success-url="/logout" />
	</security:http>

	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="admin" password="admin123" authorities="ROLE_ADMIN" />
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
</beans:beans>

Let’s see coding in action

 

Update the Controller
First two GET methods are used for business mapping like (“/home*”, “/admin*”)
Whereas other three methods are used to implemented to authenticate the users before accessing the page (“/login”, “/logout” and “/error”)

“/admin*” – this URL is intercepted by spring security framework and will be redirected to the customized login page

HomeController.java

package com.spring.series.security.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HomeController {

	// http://localhost:8080/SpringSecurity/home
	@RequestMapping(value = "/home", method = RequestMethod.GET)
	public String homePage(ModelMap modelMap) {

		modelMap.addAttribute("topic", "Welcome to the Spring Security Learning");
		modelMap.addAttribute("description", "This is HOME Page");
		return "home";
	}

	// http://localhost:8080/SpringSecurity/admin
	@RequestMapping(value = "/admin", method = RequestMethod.GET)
	public ModelAndView adminPage() {

		ModelAndView modelAndView = new ModelAndView("admin");
		modelAndView.addObject("topic", "Welcome to the Spring Security Learning");
		modelAndView.addObject("description", "This is ADMIN page");
		return modelAndView;
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public ModelAndView loginPage() {

		ModelAndView model = new ModelAndView();
		model.setViewName("login");
		model.addObject("message", "Login with Username and Password");
		return model;
	}

	@RequestMapping(value = "/logout", method = RequestMethod.GET)
	public ModelAndView logoutPage() {

		ModelAndView model = new ModelAndView();
		model.setViewName("login");
		model.addObject("message", "Logout successful");
		return model;
	}

	@RequestMapping(value = "/error", method = RequestMethod.GET)
	public ModelAndView errorPage() {

		ModelAndView model = new ModelAndView();
		model.setViewName("login");
		model.addObject("message", "Invalid Username or Password");
		return model;
	}
}

View Technologies (JSP Pages)

 

This is the customized login page that will be rendered to the user to KEY-IN credentials (username/password) for the protected access

It is important to note few variables to understand the spring security custom login form. Variable such as j_spring_security_check’, j_username, j_password in the below login.jsp are predefined in the spring security framework and we shouldn’t modify it, otherwise it won’t work as expected.

login.jsp (\src\main\webapp\WEB-INF\jsp\login.jsp)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring Security</title>
</head>
<body>
	<center>

<h1>Welcome to Spring Security Learning</h1>


<div style="text-align: center; padding: 30px; border: 1px solid; width: 250px;">

<form method="post"
				action="<c:url value='j_spring_security_check' />">

<table>

<tr>

<td colspan="2" style="color: red">${message}</td>

					</tr>


<tr>

<td>User Name:</td>


<td><input type="text" name="j_username" /></td>

					</tr>


<tr>

<td>Password:</td>


<td><input type="password" name="j_password" /></td>

					</tr>


<tr>

<td colspan="1"><input type="submit" value="Login" /></td>


<td colspan="1"><input name="reset" type="reset" /></td>

					</tr>

				</table>

			</form>

		</div>

	</center>
</body>
</html>

 

This is default page under webapp for direct access
index.jsp (\src\main\webapp\index.jsp)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!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>Spring Security</title>
</head>
<body>
	<center>
		<b>Welcome to the Spring Security Learning</b>

<h2>This is default page</h2>

	</center>
</body>
</html>

 

This is home page with two messages to display and this is not protected means when we access URL ending “/home*”, Spring Security framework won’t be intercepted
home.jsp (\src\main\webapp\WEB-INF\jsp\home.jsp)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!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>Spring Security</title>
</head>
<body>
	<center>
		<b>${topic}</b>

<h2>${description}</h2>

	</center>
</body>
</html>

 

This is admin page with protected access i.e.; only admin can access this page with correct credentials. Additionally, we got Logout link (j_spring_security_logout) to allow the logged-in user to get logged-out and disable the current session
admin.jsp (\src\main\webapp\WEB-INF\jsp\admin.jsp)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring Security</title>
</head>
<body>
	<center>
		<b>${topic}</b>

<h2>${description}</h2>


<h3>
			<a href="<c:url value="/j_spring_security_logout" />">Logout</a>
		</h3>

	</center>
</body>
</html>

Time to Test/Execute

 

Enter URL: http://localhost:8080/SpringSecurityCustomLogin/into web browser
This is default page
3_SpringSecurity_CustomLogin_default_url

 

 

Enter URL: http://localhost:8080/SpringSecurityCustomLogin/home  into web browser
Directly you get to view this page, as this is not protected (Look at the security configuration in spring-security.xml file)
4_SpringSecurity_CustomLogin_home_url

 

 

Enter URL: http://localhost:8080/SpringSecurityCustomLogin/admin into web browser
When you enter the above URL, then it redirects to URL http://localhost:8080/SpringSecurityCustomLogin/login as this is intercepted by the security filter configured

NOTE: In the previous example, when we entered “/admin” into the web browser then in that case it has redirected to http://localhost:8080/SpringSecurityCustomLogin/spring_security_login but in this case it is redirected to http://localhost:8080/SpringSecurityCustomLogin/login reason we have customized the login page overriding the default provided by the spring framework
5_SpringSecurity_CustomLogin_admin_url_login

 

 

Enter Invalid Credentials
You will get below error message and URL redirected to http://localhost:8080/SpringSecurityCustomLogin/error

NOTE: Even login fail URL got changed after customizing, compare this with previous example
6_SpringSecurity_CustomLogin_admin_url_login_fail

 

 

Enter correct credentials (admin/admin123)
Admin page with an option to Logout link, clicking which will disable the current session
7_SpringSecurity_CustomLogin_admin_url_login_success

 

 

As we have configured logout-success-url, let’s test the same
Yes, click Logout link
NOTE: the URL http://localhost:8080/SpringSecurityCustomLogin/logout
8_SpringSecurity_CustomLogin_logout_success

 

Conclusion: Thus customizing the login page overriding the default provided by the spring framework proves an edge over to design an appealing look to the page

Download project

Spring-Security-customize-login-form (7kB)

 

Read Also:

 

Happy Coding !!
Happy Learning !!

Spring Security: Hashing the password
Spring Security: Default form-based login provided by Spring framework