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 ofUserDetailsService
using anInMemoryUserDetailsManager
. We create two users (user1 and user2) with their respective passwords and the “USER” role.authenticationManager
: Creates anAuthenticationManager
instance by building anAuthenticationManagerBuilder
and configuring theuserDetailsService
andpasswordEncoder
.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
UsernamePasswordAuthenticationToken
instance with the provided username and password. - We pass the authentication token to the
AuthenticationManager
for authentication using theauthenticate
method. - If the authentication is successful, we store the
Authentication
object in theSecurityContextHolder
. - If the authentication fails with a
BadCredentialsException
, we return anUNAUTHORIZED
response.
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/authenticate
If 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.