In our previous article, we discussed how to configure Spring Security to protect our Spring MVC application. Once security is in place, accessing a protected resource redirects us to a login page. But how do we actually log in? We need a username and password, but where do these credentials come from? In this article, we’ll focus on UserDetailsService/UserDetailsManager and their role in creating and storing users in Spring Security.
Understanding UserDetails in Spring Security
Instead of manually defining a data structure to store application user information, the Spring team provides the UserDetails interface. This interface represents the essential user-related details required for authentication and authorization.
Rather than implementing UserDetails from scratch, Spring Security offers a built-in implementation called User, which already includes critical user attributes such as:
- Username
- Password
- Granted authorities (roles and permissions)
The UserDetails
Interface
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
The User
Class:
public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = 620L;
private static final Log logger = LogFactory.getLog(User.class);
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
// Constructors
// getters & setters
// ......
}
The User
class follows the Builder design pattern, making it easy to create user instances:
UserDetails user = User.withUsername("admin")
.password("admin123")
.roles("ADMIN")
.build();
The User
class simplifies user creation by allowing us to define a username, an encoded password, and assign roles or authorities in a fluent and readable way.
User Management With UserDetailsManager
Spring Security provides various implementations for managing users. The UserDetailsManager interface provides several methods to manage users, such as:
public interface UserDetailsManager extends UserDetailsService {
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
void changePassword(String oldPassword, String newPassword);
boolean userExists(String username);
}
Spring Security offers different UserDetailsManager
implementations to store user data in various ways:
In-Memory Storage (InMemoryUserDetailsManager)
This implementation stores user details in memory using a HashMap
. It is useful for prototyping and testing but is not suitable for production since data is lost when the server restarts.
Example:
Add this configuration class to your project (you can find the starter source code on GitHub), restart your application, and try logging in using the username and password used.
@Configuration
@EnableWebSecurity()
public class SecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService() throws Exception {
UserDetails user = User.withUsername("admin")
.password("admin123")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
}
This code snippet creates a UserDetailsManager with one user: (username “admin”, password “admin123”, and role “ADMIN”)
Obviously, you will encounter the following error:

The error indicates that Spring Security cannot find a PasswordEncoder. Spring Security requires a PasswordEncoder to ensure that passwords are stored securely.
Configuring Password Encoding with UserDetailsService
- BCryptPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
- NoOpPasswordEncoder (Not Secure – For Testing Only)
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
- Pbkdf2PasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return new Pbkdf2PasswordEncoder();
}
- SCryptPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder();
}
Since we passed the password directly without encrypting it when calling the password() method while creating our user, let's inform Spring Security that no encoding is being used (by simply using NoOpPasswordEncoder).
Below is the complete version of the SecurityConfig class:
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService() throws Exception {
UserDetails user = User.withUsername("admin")
.password("admin123")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Note: Spring provides alternative approaches for user storage and retrieval beyond InMemoryUserDetailsManager, such as JdbcUserDetailsManager, which we will explore in future articles. Alternatively, you can implement your custom UserDetailsService to retrieve users from any source you prefer.
You might be wondering about the appropriate scenarios to use UserDetailsService versus UserDetailsManager. This will be the subject of our next section.
UserDetailsService VS. UserDetailsManager
UserDetailsManager
is an extension of UserDetailsService
that provides CRUD operations for managing users. Unlike UserDetailsService
, which only retrieves user details, UserDetailsManager
also supports:
- Creating new users
- Updating user details
- Deleting users
- Changing passwords
Since UserDetailsManager extends UserDetailsService, it inherits the loadUserByUsername()
method but also provides user management capabilities.
🔹Use UserDetailsService when your system only authenticates users without modifying them.
🔹 Use UserDetailsManager when you need dynamic user management (e.g., allowing users to sign up, update their profile, or reset their password).
Conclusion
Spring Security provides a flexible and powerful authentication system. By using UserDetails
and UserDetailsManager
, we can create users and store them in memory, databases, or custom repositories. To enhance security, passwords should always be encoded using a secure hashing algorithm like BCrypt. With these foundational concepts, you can build robust authentication mechanisms in your Spring applications.