Two-Factor Authentication (2FA) adds an extra layer of security to your application by requiring users to provide two different authentication factors. In this tutorial, we’ll implement Two-Factor Authentication in a Spring Boot application using Spring Security.
Prerequisites
- Basic understanding of Spring Boot and Spring Security
- JDK installed on your machine
- Maven or Gradle installed
Step 1: Setup a Spring Boot Project
First, let’s create a new Spring Boot project. You can do this manually or use the Spring Initializr to generate a project with the required dependencies.
For Maven, add the following dependencies:
- Spring Web
- Spring Security
- Thymeleaf (for simple web UI)
Here’s an example pom.xml
for Maven:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Thymeleaf for simple UI -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Step 2: Create a Spring Security Configuration
Next, let’s create a Spring Security configuration class to configure our security settings.
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/login-verify").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/2fa", true)
.and()
.logout().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(User.withUsername("user")
.password("password")
.roles("USER"));
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
configure(HttpSecurity http)
method configures security rules. Here, we permit access to/login
and/login-verify
without authentication, and require authentication for all other requests. We also specify a custom login page/login
and the default success URL as/2fa
.configure(AuthenticationManagerBuilder auth)
method provides an in-memory user with usernameuser
and passwordpassword
.passwordEncoder()
method configures aNoOpPasswordEncoder
for simplicity (not recommended for production).
Step 3: Create Login and 2FA Pages
Let’s create Thymeleaf templates for the login and 2FA pages.
Create a file src/main/resources/templates/login.html
:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="/login" method="post">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Login</button>
</form>
</body>
</html>
Create a file src/main/resources/templates/2fa.html
:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>2FA Verification</title>
</head>
<body>
<h1>Two-Factor Authentication</h1>
<form action="/login-verify" method="post">
<div>
<label for="code">Enter 2FA Code:</label>
<input type="text" id="code" name="code" required autofocus>
</div>
<button type="submit">Verify</button>
</form>
</body>
</html>
Step 4: Create Controller
Now, let’s create a controller to handle login and 2FA verification.
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class AuthController {
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/2fa")
public String twoFactorAuth() {
return "2fa";
}
@PostMapping("/login-verify")
public String verify2FA() {
// Here you would implement the logic to verify the 2FA code
// For simplicity, we'll assume the code is correct
return "redirect:/welcome";
}
@GetMapping("/welcome")
public String welcome() {
return "welcome";
}
}
Step 5: Test the Application
Now you can run your Spring Boot application and access it at http://localhost:8080/login
.
- Visit
http://localhost:8080/login
to see the login page. - Enter
user
for the username andpassword
for the password. - Upon successful login, you will be redirected to the 2FA page.
- Enter any code (since we are not validating it in this example) and submit.
- You will be redirected to the
/welcome
page.
Summary
In this tutorial, we implemented Two-Factor Authentication (2FA) in a Spring Boot application using Spring Security. We created a custom login page, a 2FA verification page, and a simple controller to handle the authentication flow. In a real-world scenario, you would need to implement the logic to validate the 2FA code and enhance security practices.