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
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)
Jars Libraries Used in the Project (Maven Dependencies)
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
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
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:
- Spring Security: Default form-based login provided by Spring framework
- Spring Security: Customize Login Form
- Spring Security: Store the hashed password into MySql database
Happy Coding !!
Happy Learning !!