Spring Boot Security Form-Based Authentication Tutorial

In this tutorial, we will walk through the process of setting up form-based authentication in a Spring Boot application using the latest version of Spring Security. We'll cover creating a simple Spring Boot application, configuring Spring Security for form-based authentication, and securing a web application with a custom login page.

What is Form-Based Authentication?

Form-based authentication is a popular method in which users are required to log in through a web login form. Upon successful authentication, users are granted access to protected resources. This method allows for a customizable user experience, as the login form can be styled and designed to match the look and feel of the application.

Prerequisites

Before we start, ensure you have the following:

  • Java Development Kit (JDK) installed
  • Apache Maven installed
  • An IDE (Integrated Development Environment) like IntelliJ IDEA or Eclipse

Step 1: Setting Up the Project

Create a Spring Boot Project

  1. Open your IDE and create a new Spring Boot project using Spring Initializr.
  2. Add the following dependencies:
    • Spring Web
    • Spring Security
    • Spring Data JPA
    • H2 Database (for simplicity, but you can use any database of your choice)
    • Spring Boot Starter Thymeleaf (for creating simple views)

Project Structure

Your project structure should look like this:

spring-security-form-based-auth ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── security │ │ │ ├── SecurityConfig.java │ │ │ ├── SecurityApplication.java │ │ │ ├── model │ │ │ │ └── User.java │ │ │ ├── repository │ │ │ │ └── UserRepository.java │ │ │ ├── service │ │ │ │ └── UserService.java │ │ │ └── controller │ │ │ └── HomeController.java │ ├── main │ │ └── resources │ │ ├── templates │ │ │ ├── home.html │ │ │ ├── login.html │ │ └── application.properties └── pom.xml 

Step 2: Adding Dependencies

Add the necessary dependencies for Spring Security, Spring Data JPA, H2 database, and Thymeleaf in the pom.xml file.

pom.xml

 <dependencies> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Starter Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Spring Boot Starter Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- H2 Database --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!-- Thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Spring Boot Starter Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 

Step 3: Configuring the Application Properties

Configure the application properties for the H2 database and other settings.

application.properties

# H2 Database configuration spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.h2.console.enabled=true spring.h2.console.path=/h2-console # JPA settings spring.jpa.hibernate.ddl-auto=update # Thymeleaf settings spring.thymeleaf.cache=false 

Step 4: Creating the User Entity

Create a User entity in the com.example.security.model package.

User.java

package com.example.security.model; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String role; // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } } 

Step 5: Creating the User Repository

Create a UserRepository interface in the com.example.security.repository package.

UserRepository.java

package com.example.security.repository; import com.example.security.model.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); } 

Step 6: Creating the User Service

Create a UserService class in the com.example.security.service package.

UserService.java

package com.example.security.service; import com.example.security.model.User; import com.example.security.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.HashSet; @Service public class UserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private PasswordEncoder passwordEncoder; @PostConstruct public void init() { User admin = new User(); admin.setUsername("admin"); admin.setPassword(passwordEncoder.encode("admin")); admin.setRole("ADMIN"); userRepository.save(admin); User user = new User(); user.setUsername("user"); user.setPassword(passwordEncoder.encode("password")); user.setRole("USER"); userRepository.save(user); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { com.example.security.model.User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return User.builder() .username(user.getUsername()) .password(user.getPassword()) .roles(user.getRole()) .build(); } } 

Step 7: Configuring Spring Security

Create a SecurityConfig class in the com.example.security package to configure Spring Security for form-based authentication.

SecurityConfig.java

package com.example.security; import com.example.security.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private UserService userService; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers("/h2-console/**").permitAll() // Allow access to H2 console .requestMatchers("/", "/home").permitAll() .anyRequest().authenticated() ) .formLogin(formLogin -> formLogin .loginPage("/login") .permitAll() .defaultSuccessUrl("/home", true) ) .logout(logout -> logout.permitAll() ); // Disable CSRF and frame options for H2 console http.csrf().disable(); http.headers().frameOptions().disable(); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } } 

Step 8: Creating the Home Controller

Create a HomeController class in the com.example.security.controller package.

HomeController.java

package com.example.security.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class HomeController { @GetMapping("/") public String root() { return "index"; } @GetMapping("/home") public String home() { return "home"; } @GetMapping("/login") public String login() { return "login"; } } 

Step 9: Creating the Views

Create simple HTML views for the home page, login page, and index page in the src/main/resources/templates directory.

home.html

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Home</title> </head> <body> <h1>Welcome to the Home Page!</h1> <div> <a href="/logout" th:href="@{/logout}">Logout</a> </div> </body> </html> 

login.html

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Login</title> </head> <body> <h1>Login</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 type="submit">Login</button> </div> </form> </body> </html> 

index.html

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Index</title> </head> <body> <h1>Welcome to the Application!</h1> <div> <a href="/login" th:href="@{/login}">Login</a> </div> </body> </html> 

Step 10: Creating the Main Application Class

Create the main application class to run your Spring Boot application.

SecurityApplication.java

package com.example.security; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SecurityApplication { public static void main(String[] args) { SpringApplication.run(SecurityApplication.class, args); } } 

Step 11: Running the Application

To run the application, execute the SecurityApplication class. This will start the Spring Boot application with Spring Security configured for form-based authentication.

Accessing the Application

  1. Open your browser and navigate to http://localhost:8080/h2-console to access the H2 database console. Use the JDBC URL jdbc:h2:mem:testdb, username sa, and an empty password to log in.
  2. Navigate to http://localhost:8080/ to access the index page.
  3. Navigate to http://localhost:8080/login to access the login page. Use the credentials defined in your UserService:
    • username: admin
    • password: admin
    • username: user
    • password: password

After logging in, you will be redirected to the home page.

Conclusion

In this tutorial, we have walked through setting up a basic Spring Boot application and integrating it with Spring Security for form-based authentication. We configured Spring Security to secure our application with a custom login form and created a simple web application with Thymeleaf views. By following this tutorial, you should now have a good understanding of how to integrate Spring Security with Spring Boot and secure your web applications using form-based authentication.


Comments