docs
Interfaces and implementations for loading and managing user information from various data sources. Includes the UserDetails interface, UserDetailsService, in-memory and JDBC implementations, and reactive alternatives.
Core Capabilities:
UserDetails interface provides core user information (username, password, authorities, account status)UserDetailsService loads user by username (throws UsernameNotFoundException if not found)UserDetailsManager extends service with CRUD operations (create, update, delete, changePassword)UserDetailsPasswordService updates passwords after authentication (for encoding migration)ReactiveUserDetailsService and ReactiveUserDetailsPasswordServiceUserCache interface and CachingUserDetailsServiceKey Interfaces and Classes:
UserDetails - Core interface: getUsername(), getPassword(), getAuthorities(), account status methodsUser - Default implementation with builder pattern (User.withUsername().password().roles().build())UserDetailsService - Interface: loadUserByUsername(String) throws UsernameNotFoundExceptionUserDetailsManager - Extended interface: createUser(), updateUser(), deleteUser(), changePassword(), userExists()InMemoryUserDetailsManager - In-memory implementation (for testing/development)JdbcDaoImpl - JDBC-based UserDetailsService with customizable queriesJdbcUserDetailsManager - JDBC-based UserDetailsManager with group managementReactiveUserDetailsService - Returns Mono<UserDetails> (empty Mono if not found)UserCache - Caching interface: getUserFromCache(), putUserInCache(), removeUserFromCache()Default Behaviors:
UserDetails default methods return true for account status checks (non-expired, non-locked, etc.)User.builder() requires username and password (authorities default to empty)roles(String...) automatically prefixes with "ROLE_" (use authorities() for exact strings)JdbcDaoImpl uses default SQL queries (customizable via setter methods)InMemoryUserDetailsManager requires AuthenticationManager for changePassword() validationMono if user not found (no exception)Threading Model:
Mono<UserDetails> for non-blocking executionLifecycle:
JdbcDaoImpl implements InitializingBean (validates configuration)InMemoryUserDetailsManager stores users in memory (lost on restart)JdbcUserDetailsManager persists to databaseExceptions:
UsernameNotFoundException - Thrown when user not found (by synchronous services)Mono instead of throwingEdge Cases:
getUsername() cannot return null (required by interface)getPassword() may return null (for non-password authentication)getAuthorities() cannot return null (return empty collection)true (override in User.builder() if needed)passwordEncoder() in builder or encode before settingJdbcDaoImpl allows custom SQL via setter methodsJdbcUserDetailsManager implements GroupManager interfaceCachingUserDetailsService wrapper or set UserCache on JdbcDaoImplProvides core user information used by Spring Security.
package org.springframework.security.core.userdetails;
interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
default boolean isAccountNonExpired() { return true; }
default boolean isAccountNonLocked() { return true; }
default boolean isCredentialsNonExpired() { return true; }
default boolean isEnabled() { return true; }
}Method Details:
getAuthorities() - Returns the authorities granted to the user. Cannot return null.getPassword() - Returns the password used to authenticate. May be null or empty if not using password authentication.getUsername() - Returns the username used to authenticate. Cannot return null.isAccountNonExpired() - Returns true if the account is not expired (default: true).isAccountNonLocked() - Returns true if the account is not locked (default: true).isCredentialsNonExpired() - Returns true if the credentials have not expired (default: true).isEnabled() - Returns true if the account is enabled (default: true).Loads user-specific data during authentication.
package org.springframework.security.core.userdetails;
interface UserDetailsService {
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
}Method Details:
loadUserByUsername(String) - Locates the user based on username. Throws UsernameNotFoundException if user not found.Usage Example:
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username));
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles())
.accountExpired(!user.isActive())
.accountLocked(user.isLocked())
.disabled(!user.isEnabled())
.build();
}
}Reactive version for non-blocking user loading.
package org.springframework.security.core.userdetails;
interface ReactiveUserDetailsService {
Mono<UserDetails> findByUsername(String username);
}Method Details:
findByUsername(String) - Reactively locates user. Returns empty Mono if user not found.Usage Example:
public class CustomReactiveUserDetailsService
implements ReactiveUserDetailsService {
private final ReactiveUserRepository userRepository;
@Override
public Mono<UserDetails> findByUsername(String username) {
return userRepository.findByUsername(username)
.map(user -> User.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles())
.build());
}
}Updates user password after successful authentication.
package org.springframework.security.core.userdetails;
interface UserDetailsPasswordService {
UserDetails updatePassword(UserDetails user, String newPassword);
}Method Details:
updatePassword(UserDetails, String) - Updates the specified user with a new password. Returns updated UserDetails.Usage Example:
public class CustomUserDetailsPasswordService
implements UserDetailsPasswordService {
private final UserRepository userRepository;
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
// Update in database
userRepository.updatePassword(user.getUsername(), newPassword);
// Return updated user details
return User.withUserDetails(user)
.password(newPassword)
.build();
}
}
// Used with DaoAuthenticationProvider for password encoding migration
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setUserDetailsPasswordService(passwordService);
provider.setPasswordEncoder(newPasswordEncoder);Reactive version for password updates.
package org.springframework.security.core.userdetails;
interface ReactiveUserDetailsPasswordService {
Mono<UserDetails> updatePassword(UserDetails user, String newPassword);
}Method Details:
updatePassword(UserDetails, String) - Reactively updates password.Loads UserDetails from an Authentication token rather than username.
package org.springframework.security.core.userdetails;
interface AuthenticationUserDetailsService<T extends Authentication> {
UserDetails loadUserDetails(T token) throws UsernameNotFoundException;
}Method Details:
loadUserDetails(T) - Loads user details from an authentication token (e.g., for pre-authenticated scenarios).Checks user account status.
package org.springframework.security.core.userdetails;
interface UserDetailsChecker {
void check(UserDetails toCheck);
}Method Details:
check(UserDetails) - Examines user and throws AccountStatusException if invalid.Default implementation of UserDetails.
package org.springframework.security.core.userdetails;
class User implements UserDetails, CredentialsContainer {
User(String username, String password, Collection<? extends GrantedAuthority> authorities);
User(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities);
static UserBuilder withUsername(String username);
static UserBuilder withUserDetails(UserDetails userDetails);
@Deprecated
static UserBuilder withDefaultPasswordEncoder();
String getUsername();
String getPassword();
Collection<GrantedAuthority> getAuthorities();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
void eraseCredentials();
boolean equals(Object obj);
int hashCode();
String toString();
}Constructor Details:
Static Factory Methods:
withUsername(String) - Returns a builder for constructing users.withUserDetails(UserDetails) - Returns a builder initialized from existing user.withDefaultPasswordEncoder() - Deprecated; returns builder with default password encoder (insecure for production).Builder for creating User instances.
package org.springframework.security.core.userdetails;
class User.UserBuilder {
UserBuilder username(String username);
UserBuilder password(String password);
UserBuilder passwordEncoder(Function<String, String> encoder);
UserBuilder authorities(GrantedAuthority... authorities);
UserBuilder authorities(Collection<? extends GrantedAuthority> authorities);
UserBuilder authorities(String... authorities);
UserBuilder roles(String... roles);
UserBuilder accountExpired(boolean accountExpired);
UserBuilder accountLocked(boolean accountLocked);
UserBuilder credentialsExpired(boolean credentialsExpired);
UserBuilder disabled(boolean disabled);
UserDetails build();
}Method Details:
username(String) - Sets the username.password(String) - Sets the password.passwordEncoder(Function) - Encodes password before storing (applied at build time).authorities(GrantedAuthority...) - Sets authorities from objects.authorities(String...) - Sets authorities from strings.roles(String...) - Sets roles (automatically prefixed with "ROLE_").accountExpired(boolean) - Sets account expired status.accountLocked(boolean) - Sets account locked status.credentialsExpired(boolean) - Sets credentials expired status.disabled(boolean) - Sets disabled status.build() - Builds the UserDetails instance.Usage Example:
// Simple user creation
UserDetails user = User.withUsername("john")
.password("{bcrypt}$2a$10$...")
.authorities("ROLE_USER", "ROLE_ADMIN")
.build();
// Full user configuration
UserDetails user = User.withUsername("jane")
.password("{bcrypt}$2a$10$...")
.roles("USER", "MANAGER") // Becomes ROLE_USER, ROLE_MANAGER
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false)
.build();
// With password encoding
UserDetails user = User.withUsername("bob")
.password("plainPassword")
.passwordEncoder(passwordEncoder::encode)
.authorities("ROLE_USER")
.build();
// From existing user
UserDetails modified = User.withUserDetails(existingUser)
.password("{bcrypt}$2a$10$newPassword")
.build();Extended interface with CRUD operations for user management.
package org.springframework.security.provisioning;
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);
}Method Details:
createUser(UserDetails) - Creates a new user.updateUser(UserDetails) - Updates an existing user.deleteUser(String) - Deletes user by username.changePassword(String, String) - Changes password for current authenticated user.userExists(String) - Checks if user exists.In-memory user storage for development and testing.
package org.springframework.security.provisioning;
class InMemoryUserDetailsManager
implements UserDetailsManager, UserDetailsPasswordService {
InMemoryUserDetailsManager();
InMemoryUserDetailsManager(UserDetails... users);
InMemoryUserDetailsManager(Collection<UserDetails> users);
InMemoryUserDetailsManager(Properties users);
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
void changePassword(String oldPassword, String newPassword);
boolean userExists(String username);
UserDetails updatePassword(UserDetails user, String newPassword);
void setAuthenticationManager(AuthenticationManager authenticationManager);
}Constructor Details:
Method Details:
setAuthenticationManager(AuthenticationManager) - Required for changePassword() to validate old password.Usage Example:
// Create users
UserDetails user1 = User.withUsername("john")
.password("{bcrypt}$2a$10$...")
.roles("USER")
.build();
UserDetails user2 = User.withUsername("admin")
.password("{bcrypt}$2a$10$...")
.roles("USER", "ADMIN")
.build();
// Initialize manager
InMemoryUserDetailsManager userDetailsManager =
new InMemoryUserDetailsManager(user1, user2);
// CRUD operations
UserDetails newUser = User.withUsername("jane")
.password("{bcrypt}$2a$10$...")
.roles("USER")
.build();
userDetailsManager.createUser(newUser);
boolean exists = userDetailsManager.userExists("jane"); // true
UserDetails loaded = userDetailsManager.loadUserByUsername("jane");
userDetailsManager.deleteUser("jane");
// Change password (requires authentication manager)
userDetailsManager.setAuthenticationManager(authenticationManager);
SecurityContextHolder.getContext().setAuthentication(authentication);
try {
userDetailsManager.changePassword("oldPass", "newPass");
} catch (BadCredentialsException e) {
// Old password incorrect
log.error("Password change failed: invalid current password");
} catch (IllegalArgumentException e) {
// User not found or other validation error
log.error("Password change failed: {}", e.getMessage());
}Reactive in-memory user details service.
package org.springframework.security.core.userdetails;
class MapReactiveUserDetailsService
implements ReactiveUserDetailsService, ReactiveUserDetailsPasswordService {
MapReactiveUserDetailsService(UserDetails... users);
MapReactiveUserDetailsService(Collection<UserDetails> users);
MapReactiveUserDetailsService(Map<String, UserDetails> users);
Mono<UserDetails> findByUsername(String username);
Mono<UserDetails> updatePassword(UserDetails user, String newPassword);
}Constructor Details:
Usage Example:
UserDetails user1 = User.withUsername("john")
.password("{bcrypt}$2a$10$...")
.roles("USER")
.build();
UserDetails user2 = User.withUsername("admin")
.password("{bcrypt}$2a$10$...")
.roles("ADMIN")
.build();
MapReactiveUserDetailsService userDetailsService =
new MapReactiveUserDetailsService(user1, user2);
// Reactive loading
userDetailsService.findByUsername("john")
.doOnSuccess(user -> log.info("Loaded user: {}", user.getUsername()))
.subscribe();JDBC-based UserDetailsService implementation.
package org.springframework.security.core.userdetails.jdbc;
class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService,
MessageSourceAware, InitializingBean {
static final String DEF_USERS_BY_USERNAME_QUERY =
"select username,password,enabled from users where username = ?";
static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
"select username,authority from authorities where username = ?";
static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
"select g.id, g.group_name, ga.authority from groups g, group_members gm, " +
"group_authorities ga where gm.username = ? and g.id = ga.group_id " +
"and g.id = gm.group_id";
JdbcDaoImpl();
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
void setDataSource(DataSource dataSource);
void setUsersByUsernameQuery(String usersByUsernameQueryString);
void setAuthoritiesByUsernameQuery(String queryString);
void setGroupAuthoritiesByUsernameQuery(String queryString);
void setRolePrefix(String rolePrefix);
void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey);
void setEnableAuthorities(boolean enableAuthorities);
void setEnableGroups(boolean enableGroups);
void afterPropertiesSet();
}Default Queries:
select username,password,enabled from users where username = ?select username,authority from authorities where username = ?Method Details:
setDataSource(DataSource) - Sets the database datasource.setUsersByUsernameQuery(String) - Customizes user loading query.setAuthoritiesByUsernameQuery(String) - Customizes authority loading query.setGroupAuthoritiesByUsernameQuery(String) - Customizes group authority query.setRolePrefix(String) - Sets prefix for roles (default: "ROLE_").setEnableAuthorities(boolean) - Enables/disables direct authority loading (default: true).setEnableGroups(boolean) - Enables/disables group authority loading (default: false).Usage Example:
// Basic configuration
JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
jdbcDao.setDataSource(dataSource);
// Custom queries
jdbcDao.setUsersByUsernameQuery(
"select email as username, password_hash as password, active as enabled " +
"from app_users where email = ?");
jdbcDao.setAuthoritiesByUsernameQuery(
"select email as username, role as authority " +
"from user_roles where email = ?");
// Enable groups
jdbcDao.setEnableGroups(true);
// Use in authentication
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(jdbcDao);JDBC-based UserDetailsManager with full CRUD support and group management.
package org.springframework.security.provisioning;
class JdbcUserDetailsManager extends JdbcDaoImpl
implements UserDetailsManager, GroupManager {
static final String DEF_CREATE_USER_SQL =
"insert into users (username, password, enabled) values (?,?,?)";
static final String DEF_DELETE_USER_SQL =
"delete from users where username = ?";
static final String DEF_UPDATE_USER_SQL =
"update users set password = ?, enabled = ? where username = ?";
static final String DEF_INSERT_AUTHORITY_SQL =
"insert into authorities (username, authority) values (?,?)";
static final String DEF_DELETE_USER_AUTHORITIES_SQL =
"delete from authorities where username = ?";
static final String DEF_USER_EXISTS_SQL =
"select username from users where username = ?";
static final String DEF_CHANGE_PASSWORD_SQL =
"update users set password = ? where username = ?";
JdbcUserDetailsManager();
JdbcUserDetailsManager(DataSource dataSource);
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
void changePassword(String oldPassword, String newPassword);
boolean userExists(String username);
// Group management
List<String> findAllGroups();
List<String> findUsersInGroup(String groupName);
void createGroup(String groupName, List<GrantedAuthority> authorities);
void deleteGroup(String groupName);
void renameGroup(String oldName, String newName);
void addUserToGroup(String username, String group);
void removeUserFromGroup(String username, String group);
List<GrantedAuthority> findGroupAuthorities(String groupName);
void addGroupAuthority(String groupName, GrantedAuthority authority);
void removeGroupAuthority(String groupName, GrantedAuthority authority);
// SQL customization
void setCreateUserSql(String createUserSql);
void setDeleteUserSql(String deleteUserSql);
void setUpdateUserSql(String updateUserSql);
void setCreateAuthoritySql(String createAuthoritySql);
void setDeleteUserAuthoritiesSql(String deleteUserAuthoritiesSql);
void setUserExistsSql(String userExistsSql);
void setChangePasswordSql(String changePasswordSql);
// ... many more SQL customization methods for groups
}Method Details:
createUser(), updateUser(), deleteUser(), userExists()changePassword()Usage Example:
// Initialize manager
JdbcUserDetailsManager userDetailsManager =
new JdbcUserDetailsManager(dataSource);
// Create user
UserDetails newUser = User.withUsername("john")
.password("{bcrypt}$2a$10$...")
.roles("USER")
.build();
userDetailsManager.createUser(newUser);
// Update user
UserDetails updated = User.withUsername("john")
.password("{bcrypt}$2a$10$newPassword")
.roles("USER", "ADMIN")
.accountLocked(true)
.build();
userDetailsManager.updateUser(updated);
// Group management
userDetailsManager.createGroup("managers",
AuthorityUtils.createAuthorityList("ROLE_MANAGER"));
userDetailsManager.addUserToGroup("john", "managers");
List<GrantedAuthority> groupAuthorities =
userDetailsManager.findGroupAuthorities("managers");
// Delete user
userDetailsManager.deleteUser("john");
// Custom SQL
userDetailsManager.setCreateUserSql(
"insert into app_users (email, password_hash, active) values (?,?,?)");Wraps UserDetailsService as an AuthenticationUserDetailsService.
package org.springframework.security.core.userdetails;
class UserDetailsByNameServiceWrapper<T extends Authentication>
implements AuthenticationUserDetailsService<T> {
UserDetailsByNameServiceWrapper();
UserDetailsByNameServiceWrapper(UserDetailsService userDetailsService);
UserDetails loadUserDetails(T authentication)
throws UsernameNotFoundException;
void setUserDetailsService(UserDetailsService userDetailsService);
}Usage Example:
UserDetailsService userDetailsService = new JdbcDaoImpl(dataSource);
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> wrapper =
new UserDetailsByNameServiceWrapper<>(userDetailsService);
// Use with pre-authenticated authentication provider
PreAuthenticatedAuthenticationToken preAuth =
new PreAuthenticatedAuthenticationToken(username, null);
UserDetails user = wrapper.loadUserDetails(preAuth);Caches UserDetails objects to reduce database lookups.
package org.springframework.security.core.userdetails.cache;
interface UserCache {
UserDetails getUserFromCache(String username);
void putUserInCache(UserDetails user);
void removeUserFromCache(String username);
}No-op implementation (caching disabled).
package org.springframework.security.core.userdetails.cache;
class NullUserCache implements UserCache {
NullUserCache();
UserDetails getUserFromCache(String username);
void putUserInCache(UserDetails user);
void removeUserFromCache(String username);
}Implementation using Spring's Cache abstraction.
package org.springframework.security.core.userdetails.cache;
class SpringCacheBasedUserCache implements UserCache, InitializingBean {
SpringCacheBasedUserCache(Cache cache);
UserDetails getUserFromCache(String username);
void putUserInCache(UserDetails user);
void removeUserFromCache(String username);
void afterPropertiesSet();
}Usage Example:
// Configure Spring cache
CacheManager cacheManager = new ConcurrentMapCacheManager("users");
Cache userCache = cacheManager.getCache("users");
// Create cache wrapper
SpringCacheBasedUserCache springUserCache =
new SpringCacheBasedUserCache(userCache);
// Use with JdbcDaoImpl
JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
jdbcDao.setDataSource(dataSource);
jdbcDao.setUserCache(springUserCache);
// Or with caching wrapper
CachingUserDetailsService cachingService =
new CachingUserDetailsService(jdbcDao);
cachingService.setUserCache(springUserCache);Thrown when user cannot be found.
package org.springframework.security.core.userdetails;
class UsernameNotFoundException extends AuthenticationException {
UsernameNotFoundException(String msg);
UsernameNotFoundException(String msg, Throwable cause);
}Usage Example:
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found with username: " + username));
}