1. Introduction
This tutorial demonstrates Spring Security Form Login with Spring Boot – how to configure form-based authentication using Spring Security in a Spring Boot application. We’ll cover setting up the required dependencies, creating a configuration class to secure the application, and customizing various aspects of the login process. This article is an up-to-date article, works with Spring Security 6.2.x.
2. Maven Dependencies
When using Spring Boot, you can simply include the spring-boot-starter-security
dependency in your pom.xml
file. This will automatically bring in Spring Security and its required dependencies:
...
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-security</artifactid>
<version>3.2.5</version>
</dependency>
3. Spring Security Configuration
3.1 Enable Spring Security
To enable Spring Security in a Spring Boot application, create a @Configuration
class and annotate it with @EnableWebSecurity
. This annotation allows Spring to find and automatically apply the class to the global Web Security configuration.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity
public class AppSecurityConfig {
// Security configuration goes here
// User details -
@Bean
public UserDetailsService userDetailsService() {}
//Filter Chain configurations
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {}
//Password Encoder
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
3.2 Configure In-Memory Authentication
For simplicity, we’ll use an in-memory authentication provider to define our users. In a real-world application, you would likely use a database or an external authentication system.
@Configuration
@EnableWebSecurity
public class AppSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user1 = User.withUsername("user1")
.password(passwordEncoder().encode("user_password_1"))
.roles("USER")
.build();
//Avoid using - User.withDefaultPasswordEncoder() as it is deprecated
UserDetails user2 = User.withUsername("user2")
.password(passwordEncoder().encode("user_password_2"))
.roles("USER")
.build();
UserDetails admin1 = User.withDefaultPasswordEncoder()
.username("admin1")
.password(passwordEncoder().encode("admin_password_1"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user1, user2, admin1);
}
//password Encoder
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//Filter Chain
}
In this example, we define three users: user1
and user2
with the USER
role, and admin
with the ADMIN
role. The User.withDefaultPasswordEncoder()
method automatically encodes the passwords using the default PasswordEncoder
configured in Spring Security.
3.3 Configure URL Authorization and Form Login
Next, we’ll configure URL authorization rules and the form-based login mechanism using the HttpSecurity
object.
@Configuration
@EnableWebSecurity
public class AppSecurityConfig {
//other configs mentioned above
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/api/admins/**").hasRole("ADMIN")
.requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN")
.requestMatchers("/anonymous/**").anonymous()
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.defaultSuccessUrl("/homepage", true)
.failureUrl("/login?error=true")
.permitAll()
)
.logout((logout) -> logout.permitAll());
return http.build();
}
}
Let’s break down this configuration:
- URL Authorization: The
authorizeHttpRequests
method configures the authorization rules for different URL patterns. In this example, we:
- Allow access to
/api/admin/**
URLs only for users with theADMIN
role. - Allow access to
/api/users/**
URLs only for users with theUSER
role. - Allow anonymous access to
/anonymous*
URLs. - Allow access to the
/login
URL for everyone (required for the login page). - Require authentication for all other URLs.
- Form Login: The
formLogin
method configures the form-based login mechanism:
loginPage("/login")
specifies the custom login page URL.defaultSuccessUrl("/homepage", true)
sets the landing page after successful login. Thetrue
flag indicates that the user should always be redirected to this URL, even if they initially requested a different URL.failureUrl("/login?error=true")
sets the URL to redirect to after a failed login attempt.permitAll()
allows public access to the login page.
- Logout: The
logout
method configures the logout mechanism and allows public access to the logout URL.
3.4 Customize Login Page
Create a login.html
file in your application’s resources/templates
directory with the following content:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login Page</title>
</head>
<body>
<h1>Login</h1>
<p th:if="${param.error}" class="error">Invalid username or password</p>
<form th:action="@{/login}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" autofocus="autofocus" />
<br />
<label for="password">Password:</label>
<input type="password" id="password" name="password" />
<br />
<input type="submit" value="Log in" />
</form>
</body>
</html>
This login page uses Thymeleaf templating to display an error message if the error
parameter is present in the URL (which happens after a failed login attempt). The th:action="@{/login}"
attribute specifies the URL to submit the form to, which matches the configured loginProcessingUrl
in Spring Security.
3.5 Customize Landing Pages
To customize the landing pages after successful or failed login attempts, you can create the corresponding HTML files in the resources/templates
directory:
homepage.html
: The landing page after successful login.login.html
: The login page with an error message displayed after a failed login attempt.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Homepage</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="row mt-5">
<div class="col-md-6 offset-md-3">
<div class="card">
<div class="card-header">
<h3 class="text-center">Welcome</h3>
</div>
<div class="card-body">
<p th:text="'Hello, ' + ${#authentication.getPrincipal().getUsername()} + '!'"></p>
<p>You have successfully logged in to our application.</p>
<div th:if="${#authorization.expression('hasRole(''ADMIN'')')}">
<p>You have administrative privileges.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
To use this homepage.html
template, make sure you have configured Thymeleaf in your Spring Boot application. You can do this by adding the spring-boot-starter-thymeleaf
dependency to your pom.xml
file. With this configuration in place, when a user successfully logs in, they will be redirected to the /homepage
URL, and the homepage.html
template will be rendered, displaying a personalized welcome message and information about their role (if they have the “ADMIN” role).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
4. Conclusion
In this tutorial, we learned how to configure form-based authentication using Spring Security in a Spring Boot application. We covered setting up the required dependencies, creating a security configuration class, defining in-memory users, configuring URL authorization rules, customizing the login process, and creating custom login and landing pages.