Spring Security: Hashing the password

In this article, we will learn about hashing the password. In the previous two examples on default form-based login and custom form-based login, both uses the password stored in the spring-security file. But storing the plain text password in the xml file isn’t a good idea

So what’s the solution to secure the password so that any intruder couldn’t use it? – Hash or encode it

There are many encoding mechanism available like MD5, SHA, BCrypt and SCrypt etc. Spring recommends using BCrypt hashing algorithm to secure the password unless you have a legacy application which enforces to abide by the hashing algorithm it is using previously

Look at the below screenshot, what eclipse has to say recommend about hashing algorithm

1_HashingAlgorithm

We will use the same application i.e.; custom form-based login to secure the password using hashing algorithm. And the hashing algorithm used for this example is BCrpyt as advised by the spring team. See here

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)

2_SpringSecurity_CustomLogin_Basic_Project_Structure_In_Eclipse

Jars Libraries Used in the Project (Maven Dependencies)

3_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>

How to get hashed or encoded password using BCryptPasswordEncoder

PasswordHashing.java

package com.spring.series.security;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

public class PasswordHashing {

		public static void main(String[] args) {

		String originalPassword = "admin123";
		PasswordEncoder encoder = new BCryptPasswordEncoder();
		String encodedPassword = encoder.encode(originalPassword);
		System.out.println("Hashed Password : " + encodedPassword);
	}
}

Result in console:

Hashed Password : $2a$10$dIJYGvKNvITeljfEgi9HgOyUZFZqWZca/vLAwHzpgTqVSe/EyXZA.

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
  • <security:password-encoder hash=“bcrypt” /> this defines the hashing algorithm used for this application
  • Carefully look at the password attribute under <security:user>, value for the password not stored in plain text like in the earlier example instead its hashed/encoded using BCryptPasswordEncoder class and its result is stored

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:password-encoder hash="bcrypt" />
			<security:user-service>
				<security:user name="admin" password="$2a$10$dIJYGvKNvITeljfEgi9HgOyUZFZqWZca/vLAwHzpgTqVSe/EyXZA." 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/admin into web browser
You get to view the customized login page
4_SpringSecurity_CustomLogin_admin_url_login

 

Enter correct credentials (admin/admin123)
Remember we have hashed/encoded and stored it in the spring-security.xml file
On successful login , you will get to see the admin page
5_SpringSecurity_CustomLogin_admin_url_login_success

 

Conclusion: Beauty!! Isn’t it ? Hashing algorithm helps to add more security to the spring web application. In the next article, we will move the users with their hashed password/ ROLES into database

Download project

Spring-Security-Hashing-the-password (5kB)

 

Read Also:

 

Happy Coding !!
Happy Learning !!

Spring Security: Store the hashed password into MySql database
Spring Security: Customize Login Form