Spring security using custom login form
Spring by default provides auto login form, Most of the real time projects use their own custom login form instead of spring provided form.
Let’s see how such custom login form can be created and used in the spring security login flow.
Project structure
Follow steps from the Spring MVC project link to setup a spring maven hello world project.
Modify a web.xml file to have spring security filter as below
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
- version="3.1">
- <display-name>Archetype Created Web Application</display-name>
- <!-- Spring MVC dispatcher servlet -->
- <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>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Loads Spring Security configuration file -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-mvc.xml,
- /WEB-INF/spring-security.xml
- </param-value>
- </context-param>
- <!-- Spring Security filter -->
- <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>
- </web-app>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Archetype Created Web Application</display-name> <!-- Spring MVC dispatcher servlet --> <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> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Loads Spring Security configuration file --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-mvc.xml, /WEB-INF/spring-security.xml </param-value> </context-param> <!-- Spring Security filter --> <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> </web-app>
Include spring security dependency in the 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>SpringProject</groupId>
- <artifactId>SpringSecurityCustomLoginForm</artifactId>
- <packaging>war</packaging>
- <version>0.0.1-SNAPSHOT</version>
- <name>SpringSecurityCustomLoginForm Maven Webapp</name>
- <url>http://maven.apache.org</url>
- <properties>
- <org.springframework.version>4.2.0.RELEASE</org.springframework.version>
- <spring-security.version>3.2.7.RELEASE</spring-security.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
- <!-- Spring MVC depends on these modules spring-core, spring-beans, spring-context, spring-web -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <!-- Spring Security Dependencies -->
- <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>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>jstl</artifactId>
- <version>1.2</version>
- </dependency>
- </dependencies>
- <build>
- <finalName>SpringSecurityHelloWorldCustomLogin</finalName>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.5.1</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </project>
<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>SpringProject</groupId> <artifactId>SpringSecurityCustomLoginForm</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>SpringSecurityCustomLoginForm Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <org.springframework.version>4.2.0.RELEASE</org.springframework.version> <spring-security.version>3.2.7.RELEASE</spring-security.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- Spring MVC depends on these modules spring-core, spring-beans, spring-context, spring-web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Spring Security Dependencies --> <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> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>SpringSecurityHelloWorldCustomLogin</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
Create our custom login.jsp page
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
- <!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>Insert title here</title>
- </head>
- <body>
- <c:if test="${error eq 'true'}">
- ${msg}
- </c:if>
- <form name='loginForm' action="<c:url value='j_spring_security_check' />"
- method='POST'>
- <table>
- <tr>
- <td>User Name:</td>
- <td><input type='text' name='j_username' value=''>
- </td>
- </tr>
- <tr>
- <td>Password:</td>
- <td><input type='password' name='j_password' />
- </td>
- </tr>
- <tr>
- <td><input name="submit" type="submit"
- value="submit" />
- </td>
- <td><input name="reset" type="reset" />
- </td>
- </tr>
- </table>
- </form>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> <!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>Insert title here</title> </head> <body> <c:if test="${error eq 'true'}"> ${msg} </c:if> <form name='loginForm' action="<c:url value='j_spring_security_check' />" method='POST'> <table> <tr> <td>User Name:</td> <td><input type='text' name='j_username' value=''> </td> </tr> <tr> <td>Password:</td> <td><input type='password' name='j_password' /> </td> </tr> <tr> <td><input name="submit" type="submit" value="submit" /> </td> <td><input name="reset" type="reset" /> </td> </tr> </table> </form> </body> </html>
Since we are creating our own jsp page for login, we need to maintain the spring security flow by using its own variables which are
User name should be stored in the parameter j_username
Password should be stored in the parameter j_password
Form submit should happen to the URL –> j_spring_security_check
Create 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>Hello World</title>
- </head>
- <body>
- <h4>Hi ${username} welcome !!</h4>
- </body>
- </html>
<%@ 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>Hello World</title> </head> <body> <h4>Hi ${username} welcome !!</h4> </body> </html>
Create spring-security.xml
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:beans="http://www.springframework.org/schema/beans" 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.2.xsd
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-3.2.xsd">
- <http auto-config='true'>
- <intercept-url pattern="/secured/*" access="ROLE_USER" />
- <form-login login-page="/login" default-target-url="/secured/home"
- authentication-failure-url="/loginError" />
- </http>
- <authentication-manager erase-credentials="true">
- <authentication-provider>
- <user-service>
- <user name="kb" password="kb1234" authorities="ROLE_USER" />
- </user-service>
- </authentication-provider>
- </authentication-manager>
- </beans:beans>
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" 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.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd"> <http auto-config='true'> <intercept-url pattern="/secured/*" access="ROLE_USER" /> <form-login login-page="/login" default-target-url="/secured/home" authentication-failure-url="/loginError" /> </http> <authentication-manager erase-credentials="true"> <authentication-provider> <user-service> <user name="kb" password="kb1234" authorities="ROLE_USER" /> </user-service> </authentication-provider> </authentication-manager> </beans:beans>
In the login-page -> specify the path of controller which returns the custom login jsp, in our case it is /login , which is defined in the login controller below and highlighted
default-target-url specifies the path of a method in controller where it has to go after successful authentication, in our case it is /secured/home which is defined in the login controller below and highlighted
authentication-failure-url specifies the path of a method in controller where it has to go if authentication is failed.
In our case it is /loginError which is defined in the login controller below and highlighted.
Note : anything under /secured is intercepted by the spring security and is accessible by only the user who has the role called “ROLE_USER”
Create the LoginController class to map the requests
- package com.kb.controllers;
- import java.security.Principal;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.ModelMap;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- @Controller
- public class LoginController {
- @RequestMapping(value="/login", method = RequestMethod.GET)
- public String login(ModelMap model) {
- return "login";
- }
- @RequestMapping(value="/secured/home", method = RequestMethod.GET)
- public String home(ModelMap model, Principal principal ) {
- String name = principal.getName();
- model.addAttribute("username", name);
- return "home";
- }
- @RequestMapping(value="/loginError", method = RequestMethod.GET)
- public String loginError(ModelMap model) {
- model.addAttribute("error", "true");
- model.addAttribute("msg", "invalid login credentials");
- return "login";
- }
- }
package com.kb.controllers; import java.security.Principal; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class LoginController { @RequestMapping(value="/login", method = RequestMethod.GET) public String login(ModelMap model) { return "login"; } @RequestMapping(value="/secured/home", method = RequestMethod.GET) public String home(ModelMap model, Principal principal ) { String name = principal.getName(); model.addAttribute("username", name); return "home"; } @RequestMapping(value="/loginError", method = RequestMethod.GET) public String loginError(ModelMap model) { model.addAttribute("error", "true"); model.addAttribute("msg", "invalid login credentials"); return "login"; } }
Now Right click on the project and run as maven install
Copy the war file from project target folder to Tomcat’s /webapps folder.
Now start the server.
Access the below url
http://localhost:8080/SpringSecurityHelloWorldCustomLogin/login
you can see our custom login page instead of spring provided login form
Now provide wrong credentials and see the error
Now provide valid credentials
Username – kb
Password – kb1234
You can see, we entered into the secured page only after providing valid credentials.