skip to content

UserDetailsService & DaoAuthenticationProvider

UserDetailsService loads a user by name and DaoAuthenticationProvider checks the password against it, with in-memory, JDBC or custom implementations. Interviewers ask you to write one over a real user table, including how you signal a disabled account.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is UserDetailsService in Spring Security, and what does loadUserByUsername return?

level: juniorimportance: must knowfreq 80%

answer

  1. one method: loadUserByUsername
  2. returns UserDetails, not a boolean
  3. throw UsernameNotFoundException, never return null
  4. carries encoded password + authorities + 4 flags
  5. retrieval only, no password check

basics

~20 s

UserDetailsService is an interface with one method, loadUserByUsername(String), that looks up a user by name and returns a UserDetails object holding the username, encoded password, and authorities (roles). It throws UsernameNotFoundException if no user exists.

solid answer

~30 s

UserDetailsService is Spring Security's single-method interface for loading user data during authentication: UserDetails loadUserByUsername(String username) throws UsernameNotFoundException. Spring calls it with the login name; you fetch the user from your store (memory, DB, LDAP) and return a UserDetails. UserDetails exposes getUsername(), getPassword() (the encoded hash), getAuthorities() (granted roles/permissions), plus four account-status flags: isEnabled, isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired. It carries no plaintext password and does no password comparison itself — that is the AuthenticationProvider's job. If the user is absent you must throw UsernameNotFoundException; you must never return null. Spring ships User.builder() (User.withUsername(...)) as a ready UserDetails implementation.

code

java · 19 lines
java
import org.springframework.security.core.userdetails.*;

public class MyUserDetailsService implements UserDetailsService {

    private final AccountRepository repo;

    public MyUserDetailsService(AccountRepository repo) { this.repo = repo; }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Account a = repo.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
        return User.withUsername(a.getUsername())
                   .password(a.getPasswordHash())   // already encoded (e.g. BCrypt)
                   .authorities(a.getAuthorities())  // e.g. "ROLE_USER"
                   .disabled(!a.isActive())
                   .build();
    }
}

go deeper

for a junior

Know the single method, that it returns UserDetails, and that it throws UsernameNotFoundException.

for a middle

Explain the four status flags and the never-return-null rule, and that password checks happen elsewhere.

for a senior

Discuss adapting a domain entity to UserDetails, authority/role prefixing, and case normalization.

for a principal

Frame it as the pluggable retrieval seam and reason about enumeration/timing implications handled downstream.

**UserDetailsService** is a core Spring Security interface (package `org.springframework.security.core.userdetails`) whose entire contract is a single method: ```java UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; ``` Its job is *retrieval only*: given a username (the login identifier the user typed), find the corresponding account and return it as a **UserDetails**. It does **not** verify the password — comparing the submitted password against the stored hash is the responsibility of the `AuthenticationProvider` (typically `DaoAuthenticationProvider`, see the related question). **What UserDetails carries** (interface `org.springframework.security.core.userdetails.UserDetails`): - `String getUsername()` — the account identifier. - `String getPassword()` — the **encoded** (hashed) password, e.g. a BCrypt hash. Never plaintext. - `Collection<? extends GrantedAuthority> getAuthorities()` — roles/permissions, e.g. `ROLE_USER`, `ROLE_ADMIN`. - Four boolean status flags: `isEnabled()`, `isAccountNonExpired()`, `isAccountNonLocked()`, `isCredentialsNonExpired()`. If any of these is false, authentication fails even with a correct password (Spring throws `DisabledException`, `AccountExpiredException`, `LockedException`, or `CredentialsExpiredException`). **Ready-made implementation:** Spring provides `org.springframework.security.core.userdetails.User` with a builder: ```java User.withUsername("alice").password(hash).roles("USER").build(); ``` **The golden rule — never return null.** If no user matches, you must throw `UsernameNotFoundException`. Returning `null` causes Spring to throw an `InternalAuthenticationServiceException` (a server-side error), not a clean authentication failure. Many developers wrap this incorrectly. **Where it fits:** During a form login, `DaoAuthenticationProvider` calls your `UserDetailsService.loadUserByUsername(...)`, then uses a `PasswordEncoder` to compare the submitted password with `userDetails.getPassword()`. If everything matches and all status flags are good, an authenticated `Authentication` is placed into the `SecurityContext`. **Common implementations you can pick from:** `InMemoryUserDetailsManager` (a Map, for demos/tests), `JdbcUserDetailsManager` (a relational schema), or a **custom** `UserDetailsService` that adapts your own domain entity — the most common choice in real apps. **Gotcha — username case and normalization:** the lookup uses whatever string arrives; if your DB is case-sensitive you may need to normalize. **Gotcha — mapping authorities:** roles must be prefixed `ROLE_` when you use `hasRole(...)`; `User.builder().roles("USER")` adds the prefix for you, while `.authorities("ROLE_USER")` does not add anything automatically. **When to use:** whenever you need username/password (form or Basic) authentication backed by your own user store. For OAuth2/OIDC login you'd instead implement `OAuth2UserService`; UserDetailsService is specifically for the DAO (username/password) flow.

  • What happens if loadUserByUsername returns null instead of throwing?
    Spring wraps it in an InternalAuthenticationServiceException — a server-side failure rather than a clean BadCredentials response. Always throw UsernameNotFoundException so the framework can handle the 'no such user' case correctly (including enumeration protection).
  • Does UserDetailsService verify the password?
    No. It only loads the user. Password comparison is done by the AuthenticationProvider (DaoAuthenticationProvider) using a PasswordEncoder against the encoded password stored in UserDetails.getPassword().

saying these in an interview costs you the question

  • Saying loadUserByUsername checks/compares the password (it only retrieves the user)
  • Returning null when the user is not found instead of throwing UsernameNotFoundException
  • Storing or returning a plaintext password in UserDetails.getPassword()
  • Confusing UserDetailsService (username/password) with OAuth2UserService (OAuth2 login)

context

open as a page

How does DaoAuthenticationProvider work, and how does it collaborate with UserDetailsService and PasswordEncoder?

level: middleimportance: must knowfreq 70%

basics

~20 s

DaoAuthenticationProvider is the AuthenticationProvider for username/password login. It calls UserDetailsService to load the user, then uses a PasswordEncoder to compare the submitted password with the stored hash. If they match and the account is enabled, it returns an authenticated token.

open as a page

Compare InMemoryUserDetailsManager, JdbcUserDetailsManager, and a custom UserDetailsService. When would you use each?

level: middleimportance: should knowfreq 55%

basics

~20 s

InMemoryUserDetailsManager stores users in a Map — good for demos and tests. JdbcUserDetailsManager persists users in a relational schema using a fixed default table layout. A custom UserDetailsService adapts your own user entity/store — the usual choice for real apps with domain-specific user data.

open as a page

How would you implement a custom UserDetailsService that maps a JPA user entity to UserDetails, including roles/authorities, and what are the pitfalls?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Implement loadUserByUsername to fetch the entity from a repository and build a UserDetails carrying the encoded password, account flags, and authorities. Map each role to a GrantedAuthority, prefixing ROLE_ if you use hasRole. Throw UsernameNotFoundException when absent, and make sure lazy role collections are loaded.

open as a page

From a security standpoint, what does DaoAuthenticationProvider do to resist username enumeration and timing attacks, and how do UserDetailsPasswordService and status flags fit in?

level: principalimportance: should knowfreq 30%

basics

~20 s

It hides whether a username exists by converting UsernameNotFoundException to BadCredentialsException (hideUserNotFoundExceptions=true), and it runs a dummy password hash for unknown users so response times don't reveal valid usernames. Account status flags block disabled/locked accounts, and UserDetailsPasswordService lets it transparently upgrade weak password hashes on login.

open as a page