Spring Boot Security Custom Form Login Example

By Dhiraj Ray, 07 December,2016  

In any web app, security has always been a great concern. Today, we will be securing our Spring MVC app using Spring Security login form feature.To build this application we will be using Spring boot to build it and hence we will have a complete javaconfig.We will also take care of protecting the app against Cross Site Request Forgery (CSRF) attacks (Detail here). Now let us build our login app. If the credentials provided in the login form is valid, then user will be redirected to next page. If not an error message will be displayed.

Environment Setup

1. JDK 8 2. Spring Boot 3. Intellij Idea/ eclipse 4. Maven

Maven Dependencies

spring-boot-starter-parent: provides useful Maven defaults. It also provides a dependency-management section so that you can omit version tags for existing dependencies.

spring-boot-starter-web: includes all the dependencies required to create a web app. This will avoid lining up different spring common project versions.

spring-boot-starter-tomcat: enable an embedded Apache Tomcat 7 instance, by default. We have overriden this by defining our version. This can be also marked as provided if you wish to deploy the war to any other standalone tomcat.

tomcat-embed-jasper: provides support for .jsp file rendering.

spring-boot-starter-security: take care of all the required dependencies related to spring security.

pom.xml
<properties> <tomcat.version>8.0.3</tomcat.version> <start-class>spring-boot-example.Application</start-class> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <!--<scope>provided</scope>--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <!--<scope>provided</scope>--> </dependency> </dependencies>

Spring Bean Configuration

SpringBootServletInitializer enables process used in Servlet 3.0 using web.xml

@SpringBootApplication: This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan.

Application.java
package com.developerstack.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @ComponentScan(basePackages = "com.developerstack") @SpringBootApplication public class Application extends SpringBootServletInitializer { private static Class applicationClass = Application.class; public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

By default Spring Boot will serve static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. But here we have defined out custom folder structure for static contents, hence it is required to tell Spring boot about how to render static content.

WebConfig.java
package com.developerstack.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler("/*.js/**").addResourceLocations("/ui/static/"); registry.addResourceHandler("/*.css/**").addResourceLocations("/ui/static/"); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("login"); registry.addViewController("/login").setViewName("login"); } @Bean public InternalResourceViewResolver setupViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix ("/ui/jsp/"); resolver.setSuffix (".jsp"); resolver.setViewClass (JstlView.class); return resolver; } }

Now let us define our main configuration for spring security - SpringSecurityConfig.java.class is annotated with @EnableWebSecurity to enable Spring Security web security support. we have extended WebSecurityConfigurerAdapter to override spring features with our custom requirements. Here we are permitting all users to have access to login and logout urls but only authorised users having role as USER should be allowed to view the dashboard page. In the mean time, we have also made configuration to secure our authentication process with CSRF attack.

SpringSecurityConfig.java
package com.developerstack.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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("**/login")).and().authorizeRequests() .antMatchers("/dashboard").hasRole("USER").and().formLogin().defaultSuccessUrl("/dashboard") .loginPage("/login").and().logout().permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("john123").password("password").roles("USER"); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/*.css"); web.ignoring().antMatchers("/*.js"); } }

Our controller class is exactly similar to controller which we defined in this article. This controller is responsible to render dashboard page with user details as a model object.

Client Side

Now let us define our login.jsp.Here we have placed a hidden input type with name="${_csrf.parameterName}" and value="${_csrf.token}" which will protect the application against Cross Site Request Forgery (CSRF) attacks.

login.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Spring Security Example</title> <link href="/bootstrap.min.css" rel="stylesheet"> <script src="/jquery-2.2.1.min.js"></script> <script src="/bootstrap.min.js"></script> </head> <body> <div class="container" style="margin:50px"> <h3>Spring Security Login Example</h3> <c:if test="${param.error ne null}"> <div style="color: red">Invalid credentials.</div> </c:if> <form action="/login" method="post"> <div class="form-group"> <label for="username">UserName: <input type="text" class="form-control" id="username" name="username"> </div> <div class="form-group"> <label for="pwd">Password:</label> <input type="password" class="form-control" id="pwd" name="password"> </div> <button type="submit" class="btn btn-success">Submit</button> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> </form> </div> </body> </html>

Following is the dashboard.jsp which will be rendered on successful login. It has also a hidden input type with name="${_csrf.parameterName}" and value="${_csrf.token}" to protect the application against Cross Site Request Forgery (CSRF) attacks.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Spring Security Example</title> <link href="/bootstrap.min.css" rel="stylesheet"> <script src="/jquery-2.2.1.min.js"></script> <script src="/bootstrap.min.js"></script> </head> <body> <div> <div class="container" style="margin:50px;border: 1px solid green;"> <div> <form action="/logout" method="post"> <button type="submit" class="btn btn-danger">Log Out</button> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form> </div> <div class="row text-center"><strong> User Details</strong></div> <div class="row" style="border:1px solid green;padding:10px"> <div class="col-md-4 text-center"><strong>Name</strong></div> <div class="col-md-4 text-center"><strong>Email</strong></div> <div class="col-md-4 text-center"><strong>Address</strong></div> </div> <c:forEach var="user" items="${users}"> <div class="row" style="border:1px solid green;padding:10px"> <div class="col-md-4 text-center">${user.name}</div> <div class="col-md-4 text-center" >${user.email}</div> <div class="col-md-4 text-center">${user.address}</div> </div> </c:forEach> </div> </div> </body> </html>

Run Application

1. Run Application.java as a java application.

2. Hit the url as http://localhost:8080/dashboard but since the user is not authorised yet, user will be redirected to /login as below:

spring-boot-security-login

3. Now enter any wrong credentials and we can see an error message as follow. This proves our authentication mechanism is working.

spring-boot-security-login-error

4. Enter the username/password as john123/password and hit enter the dashboard page will be rendered.

spring-boot-security-dashboard

5. Here we can see a logout button, once clicked the user will be successfuly logged out and redirected to the login page.

Conclusion

I hope this article served you that you were looking for. If you have anything that you want to add or share then please share it below in the comment section.

Download the source

References

Spring Security Docs

Spring Security References

Spring Boot References

Suggest more topics in suggestion section or write your own article and share with your colleagues.

Is this page helpful to you? Please give us your feedback below. We would love to hear your thoughts on these articles, it will help us improve further our learning process.

Further Reading: