Today we will learn about Spring Security Login Example
Before reading this post, please go through my previous post at “Introduction to Spring 4 Security” to get some basics.
Spring Security Login Logout Example
In this post, we are going to develop Spring 4 MVC Security Web Application to provide Login and Logout features by using In-Memory option. This example uses Spring Java Config with Spring Annotations, that means without using web.xml and Spring XML Configuration(Old Style). If you are not familiar with Spring 3.x Security Module, please go through the following posts first to taste the Spring Security Recipe.
Spring MVC Security Example using in-memory, UserDetailsService and JDBC Authentication
Spring Security in Servlet Web Application using DAO, JDBC, In-Memory authentication
Spring 4 Security Module supports the following options to store and manage User Credentials:
- In-Memory Store
- Relations Databases(RDBMS)
- No SQL Data Stores
- LDAP
We will use “In-Memory Store” option in this example. We will discuss other options in my coming posts. We are going to use Spring 4.0.2.RELEASE, Spring STS 3.7 Suite IDE, Spring TC Server 3.1 with Java 1.8 and Maven build tool to develop this example.
Spring Security Login Example
We are going to develop a Login and Logout logic using Spring 4 Security Features. The main aim of this application is that developing an application without using “web.xml” and without writing a single line of Spring XML Beans Configuration. That means we are going to use Spring Java Config feature with Spring Annotations. We will develop this application with the following features:
- Welcome Page
- Login Page
- Home Page
- Logout Feature
Please use the following steps to develop and explore this Spring 4 Security Simple Login Example.
Create a “Simple Spring Web Maven” Project in Spring STS Suite with the following details
Project Name: SpringMVCSecruityMavenApp
Update pom.xml with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev</groupId>
<artifactId>SpringMVCSecruityMavenApp</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<properties>
<java.version>1.8</java.version>
<spring.version>4.0.2.RELEASE</spring.version>
<spring.security.version>4.0.2.RELEASE</spring.security.version>
<servlet.api.version>3.1.0</servlet.api.version>
<jsp.api.version>2.2</jsp.api.version>
<jstl.version>1.2</jstl.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</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>
<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>javax.servlet-api</artifactId>
<version>${servlet.api.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp.api.version}</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMVCSecruityMavenApp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
NOTE: If you are not aware of “<failOnMissingWebXml>” flag, please read at the end of this post to get a good understanding of this element usage.
First, Develop Login Controller by using Spring’s @Controller annotation
package com.journaldev.spring.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class LoginController {
@RequestMapping(value = { "/"}, method = RequestMethod.GET)
public ModelAndView welcomePage() {
ModelAndView model = new ModelAndView();
model.setViewName("welcomePage");
return model;
}
@RequestMapping(value = { "/homePage"}, method = RequestMethod.GET)
public ModelAndView homePage() {
ModelAndView model = new ModelAndView();
model.setViewName("homePage");
return model;
}
@RequestMapping(value = "/loginPage", method = RequestMethod.GET)
public ModelAndView loginPage(@RequestParam(value = "error",required = false) String error,
@RequestParam(value = "logout", required = false) String logout) {
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid Credentials provided.");
}
if (logout != null) {
model.addObject("message", "Logged out from JournalDEV successfully.");
}
model.setViewName("loginPage");
return model;
}
}
Code Explanation: We have defined three methods in “LoginController” to handle three different kinds of Client Requests…
- welcomePage() will handle all client requests which are using “/” URI.
- homePage() will handle all client requests which are using “/homePage” URI.
- loginPage() will handle all client requests which are using “/loginPage” URI.
In loginPage(), we have taken care of handling error and logout messages.
Then develop a class “LoginSecurityConfig” to provide Login and Logout Security Features using Spring 4 Security API
LoginSecurityConfig.java
package com.journaldev.spring.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
authenticationMgr.inMemoryAuthentication()
.withUser("journaldev")
.password("jd@123")
.authorities("ROLE_USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/homePage").access("hasRole('ROLE_USER')")
.and()
.formLogin().loginPage("/loginPage")
.defaultSuccessUrl("/homePage")
.failureUrl("/loginPage?error")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/loginPage?logout");
}
}
Code Explanation:- We have defined two methods in “LoginSecurityConfig” to store and manage User Credentials and take care of Login and Logout Security features.
- @EnableWebSecurity Annotation is used to enable web security in any web application.
- @EnableWebMVCSecurity Annotation is used to enable web security in Spring MVC based web application.
NOTE: @EnableWebSecurity = @EnableWebMVCSecurity + Extra features. That’s why @EnableWebMVCSecurity Annotation is deprecated in Spring 4.x Framework.
“LoginSecurityConfig” class or any class which is designated to configure Spring Security, should extend “WebSecurityConfigurerAdapter” class or implement related interface.
configureGlobal() method is used to store and mange User Credentials.
In configureGlobal() method, we can use authorities() method to define our application Roles like “ROLE_USER”. We can also use roles() method for same purpose.
Difference between authorities() and roles() methods: authorities() needs a complete role name like “ROLE_USER” roles() needs a role name like “USER”. It will automatically add “ROLE_” value to this “USER” role name.
NOTE: We will develop another example to demonstrate Roles like “USER”,“ADMIN” in my coming posts.
Important method to take care of Login and Logout Security is configure(HttpSecurity http)
The following code snipped is used to avoid unauthorized access to “/homePage”. If you try to access this page directly, we will redirected to “/loginPage” page automatically.
.antMatchers("/homePage").access("hasRole('ROLE_USER')")
If we remove access(“hasRole(‘ROLE_USER’)”) method call, then we can access this page without login to our application.
13. We have configured login and logout features using formLogin() and logout() methods.
Enable Spring MVC Configuration LoginApplicationConfig.java
package com.journaldev.spring.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@EnableWebMvc
@Configuration
@ComponentScan({ "com.journaldev.spring.*" })
@Import(value = { LoginSecurityConfig.class })
public class LoginApplicationConfig {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
Code Explanation: We use “LoginApplicationConfig” class to define Spring MVC View Resolvers to avoid writing “web.xml” file.
- @EnableWebMvc Annotation is used to enable Spring Web MVC Application Features in Spring Framework.
- @Import Annotation is used to import Spring Security Configuration class into this class.
- @ComponentScan Annotation is used to do component scanning in the specified package. It is equal to “context:component-scan” in Spring XML Configuration.
Initialize Spring Security
package com.journaldev.spring.security.config.core;
package com.journaldev.spring.secuity.config.core;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
“SpringSecurityInitializer” is used to register the DelegatingFilterProxy to use the springSecurityFilterChain. It avoids writing Filters configuration in web.xml file.
Initialize Spring MVC Application
“SpringMVCWebAppInitializer” class is used to initialize “DispatcherServlet” without web.xml file in a Annotation based configuration.
SpringMVCWebAppInitializer.java
package com.journaldev.spring.security.config.core;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.journaldev.spring.security.config.LoginApplicationConfig;
public class SpringMVCWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { LoginApplicationConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
NOTE: When we access our application, by default SpringMVCWebAppInitializer’s getServletMappings() will allow to access root url: “/”. We can override to forward to a different URL. The Spring or Pivotal team is working this issue to avoid this much Java code by introduction an annotation. Please check this at https://jira.spring.io/browse/SPR-10359.
Develop welcomePage.jsp file
<h3>Welcome to JournalDEV Tutorials</h3>
<a href="${pageContext.request.contextPath}/loginPage">Login to Journal</a>
Develop loginPage.jsp file
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<html>
<body onload='document.loginForm.username.focus();'>
<h3>JournalDEV Tutorials</h3>
<c:if test="${not empty error}"><div>${error}</div></c:if>
<c:if test="${not empty message}"><div>${message}</div></c:if>
<form name='login' action="<c:url value='/loginPage' />" method='POST'>
<table>
<tr>
<td>UserName:</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>
</body>
</html>
Develop homepage.jsp file
<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<ul>
<li>Java 8 tutorial</li>
<li>Spring tutorial</li>
<li>Gradle tutorial</li>
<li>BigData tutorial</li>
</ul>
<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>
Final Project Structure looks like this:
Run Spring Security MVC Login Logout Example
To run this Spring Web Application, we need any Web Container which supports Spring 4 and Java 8 Environments With Servlet 3.1.0 Container.
Deploy and Run on Spring TC Server in Spring STS Suite
It automatically accesses our application welcome page url as shown below.
– click on “Login to JournalDEV” link to access login page. Spring 4 MVC
– Now, provide wrong login details and click on “Login” button.
Here we can observe this error message: “Invalid Credentials provided.” Now, provide correct login details configured in “LoginSecurityConfig” class.
After successful login to our application, we can see our Application Homepage with the “Logout” link. click on “Logout” link to logout from Application.
Here we can observe that we are Logged out from our application successfully and redirected to Login page again. We can observe some Log out successful message in this Login page.
Note on Maven and web.xml
NOTE: If we observe this example, we are not using the web.xml file right. As it is a Web Application, Maven searches for web.xml file and raises some errors if it does not find in the application. That’s to avoid Maven related issues, we need to configure “<failOnMissingWebXml>” flag in pom.xml file.
That’s it all about Spring 4 Security Module Simple Example. We will develop some more real-time useful examples in my coming posts like Managing Roles, Remember-Me Feature, WebSocket Security, and more. Please drop me a comment if you like my post or have any issues/suggestions – a Comprehensive Guide – Today we will learn about Spring Security Login Example