Prerequisites: Introduction to spring, spring boot
Spring security is a powerful security framework that provides authentication and authorization to the application. It is the de-facto standard for securing Spring-based applications and it uses servlet filters to provide authentication and authorization for applications. It can be extended to support your application requirement. It provides protection against attacks like session fixation, clickjacking, Ability to secure applications against brute force attacks. In this article, you will learn how to combine a custom login page with spring security and how spring handles Form-Based Authentication.
Steps To Create custom Login-Form With Spring Security
Firstly, create a spring boot project using Spring Initializr, provide a Group and an Artifact Id, choose the spring boot version, add Spring Web, Spring Security, and Thymeleaf as the dependencies.
This will create a Spring boot Starter Project with a pom.xml configuration file, the project structure would look something like this:
The pom.xml defines the configuration of the dependencies of the project, we don’t need to add other dependencies right now as we are using spring boot and most of the things that we need for this project are auto-configured.
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >2.6.3</ version > < relativePath /> <!-- lookup parent from repository --> </ parent > < groupId >com.gfg</ groupId > < artifactId >SpringSecurityLoginForm</ artifactId > < version >0.0.1-SNAPSHOT</ version > < name >SpringSecurityLoginForm</ name > < description >Demo project for Spring Boot</ description > < properties > < java.version >1.8</ java.version > </ properties > < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-security</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-thymeleaf</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > < dependency > < groupId >org.springframework.security</ groupId > < artifactId >spring-security-test</ artifactId > < scope >test</ scope > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-maven-plugin</ artifactId > </ plugin > </ plugins > </ build > </ project > |
The controller class handles the business-related logic, it handles requests coming from the client ( In this case the browser ) and redirects them to the view page. The LoginController class in the com.gfg.SpringSecurityLoginForm is invoked using the @Controller, it has two GET methods for two requests. The welcome method simply redirects to the welcome page and the login method redirects to the custom login page.
Java
package com.gfg.SpringSecurityLoginForm.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class LoginController { @GetMapping ( "/welcome" ) public String welcome() { return "welcome.html" ; } @GetMapping ( "/login" ) public String login() { return "login.html" ; } } |
The login.html page in the templates folder defines a custom login page with fields as username and password. The form sends a post method after submitting which sends the user input data to the spring configuration. The Theymleaf dependency helps in rendering the login page.
HTML
<!DOCTYPE html> < html > < head > < meta charset = "UTF-8" > < title >Insert title here</ title > </ head > < body > < h1 >Login page</ h1 > < form th:action = "@{/login}" method = "post" > < div >< label >Username: </ label >< input type = "text" name = "username" ></ div > < div >< label >Password: </ label >< input type = "password" name = "password" ></ div > < div >< button name = "submit" type = "submit" >Login</ button ></ div > </ form > </ body > </ html > |
The welcome.html page in the templates folder is a simple HTML file that shows a successful login message and provides a link to logout out of the application.
HTML
<!DOCTYPE html> < html > < head > < meta charset = "UTF-8" > < title >Insert title here</ title > </ head > < body > < h1 >LoggedIn Successful</ h1 > < h2 >Welcome Back Click here to < a href = "logout" >logout</ a ></ h2 > </ body > </ html > |
When you run your application for the first time without any custom configuration spring security provides a system-generated password in the console that looks something like this:
This form is de-facto for spring security, the formLogin() in the HttpSecurity class is responsible to render the login form and validate user credentials. Spring Security uses a servlet filter that intercepts all the incoming requests and redirects them to this login page. The server validates the credentials passed by the user and provides a Token for that particular session. The user id is “user” by default and a user-generated password is provided in the console.
Here’s where the magic of spring security comes into the picture, the SpringSecurityConfig class in the com.gfg.config package extends the WebSecurityConfigureAdapter class which is responsible the configuring the incoming request. The configure methods provide a chain for methods to configure, validate and assign roles to the user. The authorizeRequest() method starts the method chain, and anyRequest() defines that any incoming request will have to go through the spring security config. The formLogin() basically tells spring what type of login configuration we want, In this case, we want to make a form-based login. The permitAll() method allows any user of any role to have access to a particular page or form. The loginPage(“/login”) basically tells spring to use our custom login page that we created instead of the default page. The usernameParamter gets the data passed in the username field of the login page. The logout() logs out the page for that session.
Java
package com.gfg.SpringSecurityLoginForm.config; 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; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated().and() .formLogin().permitAll().loginPage( "/login" ) .usernameParameter( "username" ).and().logout() .logoutRequestMatcher( new AntPathRequestMatcher( "/logout" )).permitAll(); } } |
We can also define our custom login and password instead of the system-generated password. You can define the customer id and password by method a spring security property in the application.properties files in the resource folder. Mention the username and password in place of user and pass respectively.
spring.security.user.name=user
spring.security.user.password=pass
After adding all the configuration files and classes your project structure should look something like this:
Now it’s time to run your created project, run your program as a Java application, and enter the URL http://localhost:8080/welcome in your favorite browser. Type user and pass as username and password respectively.
After successful authentication spring will automatically redirect to the welcome page.
So, we have created a very basic custom Form-Based Authentication using spring security and tested it locally.