Introduction
In this article, we’ll explore how to manually authenticate a user with Spring Security, using in-memory user details. We’ll cover the necessary configuration, code examples, and best practices to ensure a secure and scalable implementation.
For demonstration purposes, we’ll use two sample users with the following credentials:
- User 1: username = “user1”, password = “password1”
- User 2: username = “user2”, password = “password2”
1. Add the required dependencies
Before we dive into the implementation details, let’s add the required dependencies to our pom.xml file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/>
</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-security</artifactId>
</dependency>
<!-- Other dependencies -->
</dependencies>Spring Security Configuration
First, let’s configure Spring Security in our SecurityConfig class with the necessary configurations required to manually authenticate a User with Spring Security. Here is the code:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
userDetailsManager.createUser(
User.withUsername("user1")
.password(passwordEncoder().encode("password1"))
.roles("USER")
.build()
);
userDetailsManager.createUser(
User.withUsername("user2")
.password(passwordEncoder().encode("password2"))
.roles("USER")
.build()
);
return userDetailsManager;
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder())
.and()
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/login", "/register", "/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.permitAll();
return http.build();
}
}In this configuration, we define the following beans:
userDetailsService: Provides an implementation ofUserDetailsServiceusing anInMemoryUserDetailsManager. We create two users (user1 and user2) with their respective passwords and the “USER” role.authenticationManager: Creates anAuthenticationManagerinstance by building anAuthenticationManagerBuilderand configuring theuserDetailsServiceandpasswordEncoder.passwordEncoder: Provides a strong password encoder implementation,BCryptPasswordEncoder.securityFilterChain: Configures the HTTP security rules, including permitting access to specific URLs like/login,/register, and/public/**, requiring authentication for all other requests, and setting up the form login and logout URLs.
2. Manually Authenticating a User in Spring Security
To manually authenticate a user, we need to create an instance of UsernamePasswordAuthenticationToken and pass it to the AuthenticationManager for authentication. Here’s an example implementation:
@RestController
public class AuthenticationController {
private final AuthenticationManager authenticationManager;
public AuthenticationController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping("/authenticate")
public ResponseEntity<String> authenticateUser(@RequestBody AuthenticationRequest request) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword());
try {
Authentication authentication = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
return ResponseEntity.ok("Authentication successful");
} catch (BadCredentialsException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password");
}
}
static class AuthenticationRequest {
private String username;
private String password;
// Getters and setters
}
}In this example, we have an AuthenticationController with a /authenticate endpoint that accepts a POST request containing the username and password in the request body.
- We create a
UsernamePasswordAuthenticationTokeninstance with the provided username and password. - We pass the authentication token to the
AuthenticationManagerfor authentication using theauthenticatemethod. - If the authentication is successful, we store the
Authenticationobject in theSecurityContextHolder. - If the authentication fails with a
BadCredentialsException, we return anUNAUTHORIZEDresponse.
Testing the Authentication
To test the authentication process, we can use a tool like Postman or cURL to send a POST request to the /authenticate endpoint with the correct or incorrect user credentials.
For example, using cURL:
# Correct credentials (user1/password1)
curl -X POST -H "Content-Type: application/json" -d '{"username":"user1","password":"password1"}' http://localhost:8080/authenticate
# Correct credentials (user2/password2)
curl -X POST -H "Content-Type: application/json" -d '{"username":"user2","password":"password2"}' http://localhost:8080/authenticate
# Incorrect credentials
curl -X POST -H "Content-Type: application/json" -d '{"username":"user1","password":"wrongpassword"}' http://localhost:8080/authenticateIf the authentication is successful, you should receive the “Authentication successful” response. If the credentials are invalid, you should receive the “Invalid username or password” response.
Conclusion
In this article, we learned how to manually authenticate a user with Spring Security 6.2.x using in-memory user details. We covered the necessary configuration, code examples, and best practices for custom authentication scenarios.
Manually authenticating a user can be useful in scenarios where you need to integrate with a custom authentication mechanism or handle advanced authentication logic. Spring Security provides a flexible and extensible architecture that allows you to customize the authentication process according to your requirements.
While we used in-memory user details for demonstration purposes, in real-world applications, you would typically use a persistent data store like a database to store and retrieve user details. Spring Security supports various data sources and provides adapters for integrating with different authentication providers and user stores.