docs
DAO (Data Access Object) authentication provides a flexible authentication mechanism using UserDetailsService to load user information from various data sources. This pattern decouples authentication logic from user data storage.
Core Capabilities:
DaoAuthenticationProvider uses UserDetailsService to load users and PasswordEncoder to verify credentialsAbstractUserDetailsAuthenticationProvider with user retrieval and password checkingUserCache to reduce database lookupsUserDetailsPasswordService (for encoding migration)CompromisedPasswordCheckerUserDetailsChecker (pre and post authentication)UserDetailsRepositoryReactiveAuthenticationManagerKey Interfaces and Classes:
AbstractUserDetailsAuthenticationProvider - Abstract base with authentication flowDaoAuthenticationProvider - Concrete implementation using UserDetailsService and PasswordEncoderUserDetailsChecker - Interface: check(UserDetails) throws AuthenticationExceptionAccountStatusUserDetailsChecker - Default implementation checking standard flagsUserDetailsPasswordService - Interface: updatePassword(UserDetails, String) returns UserDetailsCachingUserDetailsService - Decorator adding caching to any UserDetailsServiceUserCache - Caching interface (implementations: NullUserCache, SpringCacheBasedUserCache)AbstractUserDetailsReactiveAuthenticationManager - Abstract reactive baseUserDetailsRepositoryReactiveAuthenticationManager - Reactive implementationDefault Behaviors:
setEraseCredentialsAfterAuthentication())UserDetailsServicePasswordEncoder.matches(rawPassword, encodedPassword)UserDetailsPasswordService called automatically when encoding changesThreading Model:
UserDetailsService should be thread-safeMono<UserDetails>Lifecycle:
UserDetailsService required in constructor (cannot be changed after construction)PasswordEncoder set via setPasswordEncoder() (optional, delegates to database if not set)UserCache set via setUserCache() (default: NullUserCache - no caching)UserDetailsPasswordService set via setUserDetailsPasswordService() (optional)CompromisedPasswordChecker set via setCompromisedPasswordChecker() (optional)Exceptions:
UsernameNotFoundException - User not found (may be hidden if hideUserNotFoundExceptions is true)BadCredentialsException - Password doesn't matchDisabledException - Account disabled (!user.isEnabled())AccountExpiredException - Account expired (!user.isAccountNonExpired())LockedException - Account locked (!user.isAccountNonLocked())CredentialsExpiredException - Credentials expired (!user.isCredentialsNonExpired())CompromisedPasswordException - Password found in breach database (if checker configured)Edge Cases:
PasswordEncoder)BadCredentialsException) if hideUserNotFoundExceptions is trueUserDetailsPasswordService called automatically when password encoding changesMono on authentication failureAbstract base class for authentication providers that rely on retrieving UserDetails.
package org.springframework.security.authentication.dao;
abstract class AbstractUserDetailsAuthenticationProvider
implements AuthenticationProvider, InitializingBean {
AbstractUserDetailsAuthenticationProvider();
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
protected abstract UserDetails retrieveUser(
String username,
UsernamePasswordAuthenticationToken authentication
) throws AuthenticationException;
protected abstract void additionalAuthenticationChecks(
UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication
) throws AuthenticationException;
void setUserDetailsChecker(UserDetailsChecker userDetailsChecker);
void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks);
void setUserCache(UserCache userCache);
void setForcePrincipalAsString(boolean forcePrincipalAsString);
void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions);
void setEraseCredentialsAfterAuthentication(boolean eraseCredentialsAfterAuthentication);
}Key Methods:
Authentication authenticate(Authentication authentication)
throws AuthenticationExceptionprotected abstract UserDetails retrieveUser(
String username,
UsernamePasswordAuthenticationToken authentication
) throws AuthenticationExceptionprotected abstract void additionalAuthenticationChecks(
UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication
) throws AuthenticationExceptionvoid setUserDetailsChecker(UserDetailsChecker userDetailsChecker)void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks)void setUserCache(UserCache userCache)UserDetails to improve performance.void setForcePrincipalAsString(boolean forcePrincipalAsString)UserDetails object.void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions)UsernameNotFoundException is converted to BadCredentialsException (prevents user enumeration).void setEraseCredentialsAfterAuthentication(boolean eraseCredentialsAfterAuthentication)Authentication Flow:
retrieveUser()additionalAuthenticationChecks() (password verification)Authentication with authoritiesConcrete implementation using UserDetailsService for user retrieval and PasswordEncoder for credential verification.
package org.springframework.security.authentication.dao;
class DaoAuthenticationProvider
extends AbstractUserDetailsAuthenticationProvider {
DaoAuthenticationProvider();
void setUserDetailsService(UserDetailsService userDetailsService);
void setPasswordEncoder(PasswordEncoder passwordEncoder);
void setUserDetailsPasswordService(
UserDetailsPasswordService userDetailsPasswordService);
void setCompromisedPasswordChecker(
CompromisedPasswordChecker compromisedPasswordChecker);
protected final UserDetails retrieveUser(
String username,
UsernamePasswordAuthenticationToken authentication
) throws AuthenticationException;
protected void additionalAuthenticationChecks(
UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication
) throws AuthenticationException;
}Key Methods:
void setUserDetailsService(UserDetailsService userDetailsService)void setPasswordEncoder(PasswordEncoder passwordEncoder)void setUserDetailsPasswordService(
UserDetailsPasswordService userDetailsPasswordService)void setCompromisedPasswordChecker(
CompromisedPasswordChecker compromisedPasswordChecker)Configuration Example:
@Configuration
public class AuthenticationConfiguration {
@Bean
public DaoAuthenticationProvider authenticationProvider(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder
) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsPasswordService(userDetailsPasswordService());
provider.setCompromisedPasswordChecker(compromisedPasswordChecker());
provider.setUserCache(userCache());
provider.setHideUserNotFoundExceptions(true);
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsPasswordService userDetailsPasswordService() {
return new CustomUserDetailsPasswordService();
}
@Bean
public CompromisedPasswordChecker compromisedPasswordChecker() {
return new HaveIBeenPwnedRestApiPasswordChecker();
}
@Bean
public UserCache userCache() {
return new SpringCacheBasedUserCache(
cacheManager().getCache("userCache")
);
}
}Complete Example:
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return userRepository.findByUsername(username)
.map(this::convertToUserDetails)
.orElseThrow(() ->
new UsernameNotFoundException("User not found: " + username));
}
private UserDetails convertToUserDetails(UserEntity entity) {
return User.builder()
.username(entity.getUsername())
.password(entity.getPassword())
.authorities(entity.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList()))
.accountExpired(!entity.isAccountNonExpired())
.accountLocked(!entity.isAccountNonLocked())
.credentialsExpired(!entity.isCredentialsNonExpired())
.disabled(!entity.isEnabled())
.build();
}
}
@Configuration
public class SecurityConfig {
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(
CustomUserDetailsService userDetailsService
) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
// Optional: Enable user caching
provider.setUserCache(userCache());
// Optional: Enable compromised password checking
provider.setCompromisedPasswordChecker(compromisedPasswordChecker());
// Optional: Hide user not found exceptions (security)
provider.setHideUserNotFoundExceptions(true);
// Optional: Custom account status checker
provider.setUserDetailsChecker(customUserDetailsChecker());
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public UserCache userCache() {
return new SpringCacheBasedUserCache(
cacheManager().getCache("userCache")
);
}
@Bean
public CompromisedPasswordChecker compromisedPasswordChecker() {
return new HaveIBeenPwnedRestApiPasswordChecker();
}
@Bean
public UserDetailsChecker customUserDetailsChecker() {
return new AccountStatusUserDetailsChecker();
}
@Bean
public AuthenticationManager authenticationManager(
DaoAuthenticationProvider authenticationProvider
) {
return new ProviderManager(authenticationProvider);
}
}Interface for checking the validity of a UserDetails object.
package org.springframework.security.core.userdetails;
interface UserDetailsChecker {
void check(UserDetails toCheck) throws AuthenticationException;
}Key Methods:
void check(UserDetails toCheck) throws AuthenticationExceptionDefault implementation that checks account status flags.
package org.springframework.security.core.userdetails;
class AccountStatusUserDetailsChecker implements UserDetailsChecker {
AccountStatusUserDetailsChecker();
void check(UserDetails user) throws AuthenticationException;
}Checks Performed:
isAccountNonLocked() - Throws LockedException if falseisEnabled() - Throws DisabledException if falseisAccountNonExpired() - Throws AccountExpiredException if falseUsage:
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsChecker(new AccountStatusUserDetailsChecker());Custom Checker Example:
public class CustomUserDetailsChecker implements UserDetailsChecker {
@Override
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {
throw new LockedException("Account is locked");
}
if (!user.isEnabled()) {
throw new DisabledException("Account is disabled");
}
if (!user.isAccountNonExpired()) {
throw new AccountExpiredException("Account has expired");
}
// Custom checks
if (user instanceof CustomUserDetails customUser) {
if (customUser.requiresPasswordChange()) {
throw new CredentialsExpiredException("Password change required");
}
if (!customUser.hasAcceptedTerms()) {
throw new CustomAuthenticationException("Terms not accepted");
}
}
}
}Decorator that adds caching to any UserDetailsService implementation.
package org.springframework.security.core.userdetails.cache;
class CachingUserDetailsService implements UserDetailsService {
CachingUserDetailsService(UserDetailsService delegate);
void setUserCache(UserCache userCache);
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
}Constructor:
CachingUserDetailsService(UserDetailsService delegate)Configuration Example:
@Configuration
public class CachingConfiguration {
@Bean
public UserDetailsService cachedUserDetailsService(
UserDetailsService delegate,
UserCache userCache
) {
CachingUserDetailsService cachingService =
new CachingUserDetailsService(delegate);
cachingService.setUserCache(userCache);
return cachingService;
}
@Bean
public UserCache userCache(CacheManager cacheManager) {
return new SpringCacheBasedUserCache(
cacheManager.getCache("users")
);
}
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users");
}
}Interface for updating user passwords after authentication.
package org.springframework.security.core.userdetails;
interface UserDetailsPasswordService {
UserDetails updatePassword(UserDetails user, String newPassword);
}Key Methods:
UserDetails updatePassword(UserDetails user, String newPassword)UserDetails.Implementation Example:
@Service
public class JpaUserDetailsPasswordService
implements UserDetailsPasswordService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
UserEntity entity = userRepository.findByUsername(user.getUsername())
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + user.getUsername()));
entity.setPassword(newPassword);
UserEntity saved = userRepository.save(entity);
return User.builder()
.username(saved.getUsername())
.password(saved.getPassword())
.authorities(user.getAuthorities())
.accountExpired(!saved.isAccountNonExpired())
.accountLocked(!saved.isAccountNonLocked())
.credentialsExpired(!saved.isCredentialsNonExpired())
.disabled(!saved.isEnabled())
.build();
}
}
// Configuration
@Bean
public DaoAuthenticationProvider authenticationProvider(
UserDetailsService userDetailsService,
UserDetailsPasswordService passwordService
) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsPasswordService(passwordService);
return provider;
}Password Encoding Migration:
@Service
public class MigratingPasswordService implements UserDetailsPasswordService {
private final UserRepository userRepository;
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
// Called automatically when user logs in with old password encoding
// newPassword is already encoded with the new encoder
UserEntity entity = userRepository.findByUsername(user.getUsername())
.orElseThrow();
entity.setPassword(newPassword);
entity.setPasswordMigrated(true);
userRepository.save(entity);
return User.builder()
.username(user.getUsername())
.password(newPassword)
.authorities(user.getAuthorities())
.build();
}
}Abstract base for reactive authentication using UserDetails.
package org.springframework.security.authentication;
abstract class AbstractUserDetailsReactiveAuthenticationManager
implements ReactiveAuthenticationManager {
AbstractUserDetailsReactiveAuthenticationManager();
Mono<Authentication> authenticate(Authentication authentication);
protected abstract Mono<UserDetails> retrieveUser(String username);
}Key Methods:
protected abstract Mono<UserDetails> retrieveUser(String username)Reactive authentication provider using ReactiveUserDetailsService.
package org.springframework.security.authentication;
class UserDetailsRepositoryReactiveAuthenticationManager
extends AbstractUserDetailsReactiveAuthenticationManager {
UserDetailsRepositoryReactiveAuthenticationManager(
ReactiveUserDetailsService userDetailsService);
void setPasswordEncoder(PasswordEncoder passwordEncoder);
void setUserDetailsPasswordService(
ReactiveUserDetailsPasswordService passwordService);
void setCompromisedPasswordChecker(
ReactiveCompromisedPasswordChecker compromisedPasswordChecker);
}Key Methods:
void setPasswordEncoder(PasswordEncoder passwordEncoder)void setUserDetailsPasswordService(
ReactiveUserDetailsPasswordService passwordService)Configuration Example:
@Configuration
public class ReactiveAuthConfig {
@Bean
public ReactiveAuthenticationManager authenticationManager(
ReactiveUserDetailsService userDetailsService
) {
UserDetailsRepositoryReactiveAuthenticationManager manager =
new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
manager.setPasswordEncoder(passwordEncoder());
manager.setUserDetailsPasswordService(reactivePasswordService());
manager.setCompromisedPasswordChecker(reactiveCompromisedPasswordChecker());
return manager;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public ReactiveUserDetailsPasswordService reactivePasswordService() {
return new CustomReactivePasswordService();
}
@Bean
public ReactiveCompromisedPasswordChecker reactiveCompromisedPasswordChecker() {
return new ReactiveHaveIBeenPwnedPasswordChecker();
}
}// Domain Model
@Entity
@Table(name = "users")
public class UserEntity {
@Id
private String username;
private String password;
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
@ElementCollection(fetch = FetchType.EAGER)
private Set<String> roles;
// getters and setters
}
// Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
Optional<UserEntity> findByUsername(String username);
}
// UserDetailsService Implementation
@Service
public class DatabaseUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
UserEntity user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(username));
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.toList())
.accountExpired(!user.isAccountNonExpired())
.accountLocked(!user.isAccountNonLocked())
.credentialsExpired(!user.isCredentialsNonExpired())
.disabled(!user.isEnabled())
.build();
}
}
// Password Service Implementation
@Service
public class DatabaseUserDetailsPasswordService
implements UserDetailsPasswordService {
private final UserRepository userRepository;
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
UserEntity entity = userRepository.findByUsername(user.getUsername())
.orElseThrow();
entity.setPassword(newPassword);
userRepository.save(entity);
return User.builder()
.username(user.getUsername())
.password(newPassword)
.authorities(user.getAuthorities())
.build();
}
}
// Security Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public DaoAuthenticationProvider authenticationProvider(
DatabaseUserDetailsService userDetailsService,
DatabaseUserDetailsPasswordService passwordService
) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsPasswordService(passwordService);
provider.setUserCache(userCache());
provider.setHideUserNotFoundExceptions(true);
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public UserCache userCache() {
return new SpringCacheBasedUserCache(
cacheManager().getCache("users")
);
}
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider(null, null))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
}org.springframework.security.authentication.daoorg.springframework.security.core.userdetailsorg.springframework.security.core.userdetails.cache