Week 6 Day 2 — Users, Passwords, and Authentication Flow
Goal
Today I want to understand username/password authentication in Spring Security.
Main questions:
- What is a user in Spring Security?
- What is
UserDetails? - What is
UserDetailsService? - What is
AuthenticationManager? - What is
AuthenticationProvider? - What is
DaoAuthenticationProvider? - What is
PasswordEncoder? - Why must passwords be hashed?
- How does database login work?
- How do roles and authorities work?
- How do I create a custom
UserDetailsService? - What are common exam traps?
1. Quick Review from Week 6 Day 1
In Day 1, I learned:
- Spring Security protects requests before controllers.
- Authentication means proving identity.
- Authorization means checking permissions.
- 401 means not authenticated.
- 403 means authenticated but not allowed.
- Spring Security is filter-chain based.
SecurityFilterChaindefines security rules.HttpSecuritybuilds theSecurityFilterChain.SecurityContextHolderstores the current security context.Authenticationrepresents the current authenticated user.PasswordEncoderstores and verifies password hashes.- Do not store raw passwords.
Memory sentence:
Authentication = who are you?
Authorization = what can you do?
Today I go deeper into username/password authentication.
2. Big Authentication Flow
When a user logs in with username and password, the simplified flow is:
1. User sends username and password.
2. Spring Security creates an unauthenticated Authentication object.
3. AuthenticationManager receives it.
4. AuthenticationManager delegates to AuthenticationProvider.
5. DaoAuthenticationProvider loads user via UserDetailsService.
6. UserDetailsService loads user from memory or database.
7. PasswordEncoder compares raw password with stored password hash.
8. If valid, authenticated Authentication is created.
9. Authentication is stored in SecurityContext.
10. User is now authenticated for the request.
Short memory version:
Credentials -> AuthenticationManager -> AuthenticationProvider -> UserDetailsService -> PasswordEncoder -> SecurityContext
3. What Is a User in Spring Security?
In my business database, a user may look like this:
@Entity
@Table(name = "users")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String passwordHash;
private String role;
private boolean enabled;
}
But Spring Security does not directly require my entity.
Spring Security needs a security representation of the user.
That representation is usually:
UserDetails
Memory sentence:
My database user is my model.
UserDetailsis Spring Security’s user model.
4. UserDetails
UserDetails represents user information needed for authentication.
It contains:
username
password
authorities
account status flags
Important methods:
String getUsername();
String getPassword();
Collection<? extends GrantedAuthority> getAuthorities();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
Memory sentence:
UserDetailsis the user data Spring Security needs for login.
5. UserDetails Is Not Necessarily My Entity
I should not confuse:
UserEntity
UserDetails
UserEntity is for my database.
UserDetails is for Spring Security authentication.
Example conversion:
UserEntity -> UserDetails
This conversion usually happens inside:
UserDetailsService
Memory sentence:
UserDetailsServiceconverts my stored user into Spring Security user details.
6. Built-In User Class
Spring Security provides a built-in implementation:
org.springframework.security.core.userdetails.User
Example:
UserDetails userDetails = User.builder()
.username("user@example.com")
.password(passwordHash)
.roles("USER")
.build();
This is useful when I do not want to create my own custom UserDetails class.
Memory sentence:
Spring Security’s
Useris a simple built-inUserDetailsimplementation.
7. UserDetailsService
UserDetailsService loads a user by username.
Its main method is:
UserDetails loadUserByUsername(String username);
Simple definition:
UserDetailsServiceis the service Spring Security uses to load user data during username/password authentication.
Example:
@Service
public class DatabaseUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public DatabaseUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) {
UserEntity user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email));
return User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole())
.disabled(!user.isEnabled())
.build();
}
}
Memory sentence:
UserDetailsServiceloads the user for authentication.
8. Why the Method Says Username
The method is called:
loadUserByUsername(String username)
But I can use email as username.
Example:
username = email
So this is okay:
loadUserByUsername(String email)
Memory sentence:
In Spring Security, “username” can be an email, login name, or another unique identifier.
9. UsernameNotFoundException
If the user does not exist, throw:
UsernameNotFoundException
Example:
UserEntity user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email));
This tells Spring Security:
No user found with this username.
Memory sentence:
If the login user is not found, throw
UsernameNotFoundException.
10. GrantedAuthority
An authority is a permission granted to a user.
Example authorities:
ROLE_USER
ROLE_ADMIN
TASK_READ
TASK_WRITE
INVOICE_APPROVE
In Spring Security, roles are also authorities.
A role usually has prefix:
ROLE_
Memory sentence:
Authorities are permissions. Roles are authorities with
ROLE_prefix.
11. Roles
When I write:
.roles("USER")
Spring Security creates authority:
ROLE_USER
When I write:
.roles("ADMIN")
Spring Security creates:
ROLE_ADMIN
Then this works:
.hasRole("ADMIN")
because it checks for:
ROLE_ADMIN
Memory sentence:
roles("ADMIN")createsROLE_ADMIN.
12. Authorities
If I want exact permissions, I can use authorities.
Example:
.authorities("TASK_READ", "TASK_WRITE")
Then security rule:
.hasAuthority("TASK_READ")
Roles are broad.
Authorities can be more specific.
Example:
ROLE_ADMIN
TASK_READ
TASK_WRITE
TASK_DELETE
INVOICE_APPROVE
Memory sentence:
Roles are broad groups; authorities can be fine-grained permissions.
13. Role Trap
Wrong:
User.builder()
.username("admin@example.com")
.password(hash)
.roles("ROLE_ADMIN")
.build();
Why wrong?
roles() adds ROLE_ automatically.
This can become ROLE_ROLE_ADMIN.
Correct:
.roles("ADMIN")
or use exact authority:
.authorities("ROLE_ADMIN")
Memory sentence:
With
roles(), do not includeROLE_.
14. PasswordEncoder
PasswordEncoder is used to hash and verify passwords.
Example bean:
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
During registration:
String passwordHash = passwordEncoder.encode(rawPassword);
During login:
passwordEncoder.matches(rawPassword, storedPasswordHash);
Memory sentence:
PasswordEncoderhashes raw passwords and verifies login passwords.
15. Never Store Raw Passwords
Bad database row:
email = user@example.com
password = secret123
Good database row:
email = user@example.com
password_hash = $2a$10$...
Why?
If the database is leaked:
raw passwords expose users immediately
hashes reduce damage
Memory sentence:
Store password hashes, never raw passwords.
16. Hashing Is One-Way
Password hashing is one-way.
Meaning:
raw password -> hash
hash -> raw password should not be possible
Login does not decode the password.
Instead:
compare raw password with stored hash using matches()
Example:
boolean ok = passwordEncoder.matches("secret123", storedHash);
Memory sentence:
Passwords are verified, not decoded.
17. BCrypt
A common password encoder is:
BCryptPasswordEncoder
Example:
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
BCrypt is intentionally slow.
Why?
slow hashing makes brute-force attacks harder
Memory sentence:
Password hashing should be slow enough to resist brute-force attacks.
18. NoOpPasswordEncoder Warning
You may see examples with:
NoOpPasswordEncoder
or passwords like:
{noop}password
This means:
no password hashing
plain text password
Use only for tiny demos, never real apps.
Memory sentence:
noopmeans no hashing. Do not use it in real applications.
19. Authentication
Authentication represents authentication information.
Before authentication:
principal = username
credentials = raw password
authenticated = false
After authentication:
principal = UserDetails
credentials = usually cleared
authorities = roles/permissions
authenticated = true
Memory sentence:
Authenticationcan represent both the login attempt and the authenticated user.
20. UsernamePasswordAuthenticationToken
For username/password login, Spring Security often uses:
UsernamePasswordAuthenticationToken
Before authentication:
new UsernamePasswordAuthenticationToken(email, rawPassword)
After authentication:
principal = UserDetails
authorities = loaded roles
authenticated = true
Memory sentence:
UsernamePasswordAuthenticationTokencarries username/password authentication data.
21. AuthenticationManager
AuthenticationManager is responsible for authenticating.
Simple definition:
AuthenticationManagerreceives an authentication request and returns an authenticated result or throws an exception.
Example mental model:
AuthenticationManager.authenticate(authentication)
If success:
returns authenticated Authentication
If failure:
throws AuthenticationException
Memory sentence:
AuthenticationManagerdecides whether authentication succeeds.
22. AuthenticationProvider
AuthenticationProvider performs a specific authentication strategy.
Examples:
username/password
JWT token
LDAP
OAuth2
API key
For username/password, the common provider is:
DaoAuthenticationProvider
Memory sentence:
AuthenticationProviderknows how to authenticate one kind of credential.
23. DaoAuthenticationProvider
DaoAuthenticationProvider authenticates username/password using:
UserDetailsService
PasswordEncoder
Flow:
1. Receive username/password authentication token.
2. Use UserDetailsService to load user by username.
3. Read stored password hash from UserDetails.
4. Use PasswordEncoder to compare raw password with stored hash.
5. If valid, return authenticated Authentication.
6. If invalid, throw authentication exception.
Memory sentence:
DaoAuthenticationProviderusesUserDetailsServiceplusPasswordEncoder.
24. Full Username/Password Authentication Flow
Detailed flow:
1. User submits email and password.
2. Authentication filter reads credentials.
3. Filter creates UsernamePasswordAuthenticationToken.
4. AuthenticationManager receives the token.
5. ProviderManager delegates to DaoAuthenticationProvider.
6. DaoAuthenticationProvider calls UserDetailsService.
7. UserDetailsService loads user from database.
8. DaoAuthenticationProvider checks password with PasswordEncoder.
9. If password is valid, authenticated Authentication is returned.
10. SecurityContextHolder stores Authentication.
11. Authorization rules can now check roles/authorities.
12. Request can continue.
Short memory:
Filter -> AuthenticationManager -> DaoAuthenticationProvider -> UserDetailsService -> PasswordEncoder -> SecurityContext
25. Database User Entity
Example entity:
@Entity
@Table(name = "users")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
@Column(name = "password_hash", nullable = false)
private String passwordHash;
@Column(nullable = false)
private String role;
@Column(nullable = false)
private boolean enabled = true;
protected UserEntity() {
}
public UserEntity(String email, String passwordHash, String role) {
this.email = email;
this.passwordHash = passwordHash;
this.role = role;
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public String getPasswordHash() {
return passwordHash;
}
public String getRole() {
return role;
}
public boolean isEnabled() {
return enabled;
}
}
Important:
Database stores password hash, not raw password.
26. User Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByEmail(String email);
boolean existsByEmail(String email);
}
Used for:
load user during login
check duplicate email during registration
Memory sentence:
User repository loads users from the database.
27. Database UserDetailsService
@Service
public class DatabaseUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public DatabaseUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) {
UserEntity user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email));
return User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole())
.disabled(!user.isEnabled())
.build();
}
}
If database role is:
USER
then:
.roles(user.getRole())
creates authority:
ROLE_USER
Important:
Store role as USER or ADMIN if using roles().
Do not store ROLE_USER unless using authorities().
28. Security Config with Database Users
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Because I have a UserDetailsService bean, Spring Security can use it for username/password authentication.
Memory sentence:
Define
UserDetailsServiceandPasswordEncoderfor database-backed username/password login.
29. Registration Flow
Registration means creating a new user.
Request DTO:
public record RegisterRequest(
@NotBlank
@Email
String email,
@NotBlank
@Size(min = 8)
String password
) {
}
Service:
@Service
public class AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public AuthService(
UserRepository userRepository,
PasswordEncoder passwordEncoder
) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Transactional
public void register(RegisterRequest request) {
if (userRepository.existsByEmail(request.email())) {
throw new EmailAlreadyUsedException(request.email());
}
String passwordHash = passwordEncoder.encode(request.password());
UserEntity user = new UserEntity(
request.email(),
passwordHash,
"USER"
);
userRepository.save(user);
}
}
Memory sentence:
During registration, encode the raw password before saving.
30. Login Flow with HTTP Basic
With HTTP Basic, I do not normally write a login controller.
Client sends:
Authorization: Basic base64(email:password)
Spring Security:
extracts credentials
loads user
checks password
sets SecurityContext
authorizes request
This is good for learning and simple internal APIs.
For public REST APIs, JWT or OAuth2 is more common.
Memory sentence:
HTTP Basic login is handled by Spring Security filters, not my controller.
31. Login Flow with Custom Login Endpoint
Sometimes in REST APIs, I create:
POST /api/auth/login
The endpoint receives email and password, authenticates them, and returns a token.
Request:
public record LoginRequest(
@NotBlank
@Email
String email,
@NotBlank
String password
) {
}
Controller:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
public AuthController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping("/login")
public LoginResponse login(@Valid @RequestBody LoginRequest request) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.email(),
request.password()
)
);
return new LoginResponse(
authentication.getName(),
"token-will-come-later"
);
}
}
Response:
public record LoginResponse(
String username,
String token
) {
}
Important:
This checks username/password using Spring Security.
Later, JWT lesson will replace token-will-come-later with real JWT.
32. Exposing AuthenticationManager
In many modern configurations, I can expose AuthenticationManager like this:
@Bean
AuthenticationManager authenticationManager(
AuthenticationConfiguration configuration
) throws Exception {
return configuration.getAuthenticationManager();
}
Full config part:
@Configuration
public class SecurityConfig {
@Bean
AuthenticationManager authenticationManager(
AuthenticationConfiguration configuration
) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Memory sentence:
A custom login endpoint can use
AuthenticationManagerto authenticate credentials.
33. What Happens on Bad Password?
User enters wrong password.
Flow:
1. UserDetailsService loads user.
2. PasswordEncoder.matches(raw, hash) returns false.
3. Authentication fails.
4. Spring Security throws authentication exception.
5. Response is usually 401.
Memory sentence:
Wrong password means authentication failure, usually 401.
34. Disabled User
If the user is disabled:
.disabled(!user.isEnabled())
then Spring Security can reject login.
Database:
enabled = false
Result:
authentication fails
Memory sentence:
UserDetailsaccount flags can prevent login.
35. Account Status Flags
UserDetails supports:
enabled
account non-expired
account non-locked
credentials non-expired
Meaning:
| Flag | Meaning |
|---|---|
| enabled | user is active |
| account non-expired | account is not expired |
| account non-locked | account is not locked |
| credentials non-expired | password is not expired |
For many simple apps, only enabled is used first.
Memory sentence:
User account flags can block authentication.
36. Custom UserDetails Class
Sometimes I need user ID in the principal.
Create custom principal:
public class AppUserPrincipal implements UserDetails {
private final Long id;
private final String email;
private final String passwordHash;
private final Collection<? extends GrantedAuthority> authorities;
private final boolean enabled;
public AppUserPrincipal(
Long id,
String email,
String passwordHash,
Collection<? extends GrantedAuthority> authorities,
boolean enabled
) {
this.id = id;
this.email = email;
this.passwordHash = passwordHash;
this.authorities = authorities;
this.enabled = enabled;
}
public Long getId() {
return id;
}
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword() {
return passwordHash;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
Then:
return new AppUserPrincipal(
user.getId(),
user.getEmail(),
user.getPasswordHash(),
List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole())),
user.isEnabled()
);
Memory sentence:
Custom
UserDetailsis useful when I need extra principal data like user ID.
37. Getting Custom Principal in Controller
If principal is custom:
@GetMapping("/api/me")
public MeDto me(@AuthenticationPrincipal AppUserPrincipal principal) {
return new MeDto(
principal.getId(),
principal.getUsername()
);
}
DTO:
public record MeDto(
Long id,
String email
) {
}
Memory sentence:
@AuthenticationPrincipalinjects the current principal.
38. Principal vs Authentication vs @AuthenticationPrincipal
Controller options:
@GetMapping("/api/me1")
public String me1(Principal principal) {
return principal.getName();
}
@GetMapping("/api/me2")
public String me2(Authentication authentication) {
return authentication.getName();
}
@GetMapping("/api/me3")
public MeDto me3(@AuthenticationPrincipal AppUserPrincipal principal) {
return new MeDto(principal.getId(), principal.getUsername());
}
Use:
Principal for simple username
Authentication for authorities/details
@AuthenticationPrincipal for custom principal data
39. SecurityContext in Service
Sometimes service needs current user.
Simple option:
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
String email = authentication.getName();
But do not spread this everywhere.
Better design:
controller reads current principal
passes user ID/email to service
or use a small CurrentUserService abstraction
Example:
@Service
public class CurrentUserService {
public String currentUsername() {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
return authentication.getName();
}
}
Memory sentence:
Avoid random
SecurityContextHoldercalls everywhere; centralize current-user access.
40. Security + Database Flow Example
Endpoint:
GET /api/tasks
Authorization: Basic ...
Flow:
1. BasicAuthenticationFilter reads Authorization header.
2. It creates username/password authentication request.
3. AuthenticationManager authenticates.
4. DaoAuthenticationProvider loads user with UserDetailsService.
5. UserRepository finds user by email.
6. PasswordEncoder checks password.
7. Authentication is stored in SecurityContext.
8. Authorization checks endpoint rules.
9. Controller runs.
10. Controller/service can read current user.
Memory sentence:
Login uses the user repository indirectly through
UserDetailsService.
41. Bad Practice: Returning Password Hash
Bad DTO:
public record UserDto(
Long id,
String email,
String passwordHash
) {
}
Never expose password hash in API responses.
Better:
public record UserDto(
Long id,
String email,
String role
) {
}
Memory sentence:
Do not return password hashes to clients.
42. Bad Practice: Logging Passwords
Bad:
log.info("Login request: {}", request);
If request contains password, it may log raw password.
Better:
log.info("Login attempt for email={}", request.email());
Memory sentence:
Never log raw passwords.
43. Bad Practice: Registering User Without Encoding Password
Bad:
UserEntity user = new UserEntity(
request.email(),
request.password(),
"USER"
);
This stores raw password.
Correct:
String passwordHash = passwordEncoder.encode(request.password());
UserEntity user = new UserEntity(
request.email(),
passwordHash,
"USER"
);
Memory sentence:
Always encode password before saving.
44. Bad Practice: Manual Password Check in Controller
Bad:
@PostMapping("/login")
public String login(@RequestBody LoginRequest request) {
UserEntity user = userRepository.findByEmail(request.email()).orElseThrow();
if (!user.getPasswordHash().equals(request.password())) {
throw new RuntimeException("Bad password");
}
return "ok";
}
Problems:
compares raw password incorrectly
bypasses PasswordEncoder
bypasses Spring Security authentication flow
bad error handling
unsafe
Correct:
Use AuthenticationManager or Spring Security filters.
Memory sentence:
Do not manually compare passwords in controllers.
45. Common Exam Traps
Trap 1
UserDetailsService loads user data; it does not check passwords by itself.
Trap 2
DaoAuthenticationProvider uses UserDetailsService and PasswordEncoder.
Trap 3
PasswordEncoder is one-way. It does not decode passwords.
Trap 4
Store password hashes, not raw passwords.
Trap 5
roles("ADMIN") creates ROLE_ADMIN.
Trap 6
Do not use roles("ROLE_ADMIN").
Trap 7
hasRole("ADMIN") checks for ROLE_ADMIN.
Trap 8
hasAuthority("ROLE_ADMIN") checks exactly ROLE_ADMIN.
Trap 9
UsernameNotFoundException is used when user cannot be loaded.
Trap 10
Wrong password usually causes authentication failure and 401.
Trap 11
User database entity and UserDetails are not the same thing.
Trap 12
AuthenticationManager authenticates credentials.
Trap 13
AuthenticationProvider performs a specific authentication strategy.
Trap 14
Custom login endpoints can use AuthenticationManager.
Trap 15
Never return or log password hashes or raw passwords.
46. Real Exam Question: UserDetails
Question:
What is UserDetails?
Answer:
UserDetails is Spring Security’s representation of user information needed for authentication, such as username, password, authorities, and account status flags.
47. Real Exam Question: UserDetailsService
Question:
What does UserDetailsService do?
Answer:
UserDetailsService loads a user by username and returns a UserDetails object for authentication.
48. Real Exam Question: PasswordEncoder
Question:
What is PasswordEncoder used for?
Answer:
PasswordEncoder hashes raw passwords and verifies raw login passwords against stored password hashes.
49. Real Exam Question: AuthenticationManager
Question:
What does AuthenticationManager do?
Answer:
AuthenticationManager receives an authentication request and returns an authenticated Authentication if credentials are valid, or throws an authentication exception if they are invalid.
50. Real Exam Question: DaoAuthenticationProvider
Question:
What does DaoAuthenticationProvider use?
Answer:
It uses UserDetailsService to load the user and PasswordEncoder to verify the password.
51. Real Exam Question: Role Prefix
Question:
What does roles("ADMIN") create?
Answer:
It creates the authority ROLE_ADMIN.
52. Real Exam Question: hasRole
Question:
What does hasRole("ADMIN") check?
Answer:
It checks whether the user has authority ROLE_ADMIN.
53. Real Exam Question: Password Hashing
Question:
Should passwords be stored raw or hashed?
Answer:
Passwords should be stored as hashes, never as raw text.
54. Real Exam Question: @AuthenticationPrincipal
Question:
What does @AuthenticationPrincipal do?
Answer:
It injects the current authenticated principal into a controller method parameter.
55. Interview Answer
Question:
Explain the username/password authentication flow in Spring Security.
Good answer:
When a user submits username and password, Spring Security creates an authentication request, usually a UsernamePasswordAuthenticationToken. The AuthenticationManager receives it and delegates to an AuthenticationProvider, commonly DaoAuthenticationProvider. The provider uses UserDetailsService to load the user and PasswordEncoder to compare the raw password with the stored password hash. If authentication succeeds, Spring Security creates an authenticated Authentication and stores it in the SecurityContext.
56. Interview Answer
Question:
What is the role of
UserDetailsService?
Good answer:
UserDetailsService loads user information by username. In a database-backed application, it usually queries the user repository, finds the user by email or username, and converts the database user into a UserDetails object with username, password hash, roles, authorities, and account status flags.
57. Interview Answer
Question:
Why do we need
PasswordEncoder?
Good answer:
We need PasswordEncoder because passwords should never be stored as raw text. A password encoder hashes the password before saving and later verifies a raw login password against the stored hash. It is one-way, so it does not decode passwords. This reduces risk if the database is leaked.
58. Interview Answer
Question:
What is the difference between role and authority?
Good answer:
An authority is a permission in Spring Security. A role is a special kind of authority that usually has the ROLE_ prefix. For example, roles("ADMIN") creates the authority ROLE_ADMIN, and hasRole("ADMIN") checks for ROLE_ADMIN. For fine-grained permissions, I can use authorities such as TASK_READ or INVOICE_APPROVE.
59. Interview Answer
Question:
How would you implement database login?
Good answer:
I would store users in a database with a unique email, password hash, role or authorities, and enabled flag. I would implement UserDetailsService to load the user from the repository and return UserDetails. I would define a PasswordEncoder, usually BCrypt. Then Spring Security’s DaoAuthenticationProvider can use the UserDetailsService and PasswordEncoder to authenticate username/password login.
60. Tiny Code Practice
Create repository:
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByEmail(String email);
}
Create UserDetailsService:
@Service
public class DatabaseUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public DatabaseUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) {
UserEntity user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email));
return User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole())
.disabled(!user.isEnabled())
.build();
}
}
Questions:
- What does
findByEmailreturn? - What happens if user is not found?
- What does
.password(...)expect? - What does
.roles("USER")create? - What does
.disabled(!user.isEnabled())do?
Answers:
Optional<UserEntity>.UsernameNotFoundExceptionis thrown.- The stored encoded password hash.
- Authority
ROLE_USER. - It disables login if the user is not enabled.
61. Tiny Bug Practice 1
Problem:
return User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles("ROLE_ADMIN")
.build();
Question:
What is wrong?
Answer:
roles() adds the ROLE_ prefix automatically. This can create ROLE_ROLE_ADMIN.
Correct:
.roles("ADMIN")
or:
.authorities("ROLE_ADMIN")
62. Tiny Bug Practice 2
Problem:
String passwordHash = request.password();
userRepository.save(new UserEntity(request.email(), passwordHash, "USER"));
Question:
What is wrong?
Answer:
The raw password is saved directly. It must be encoded first.
Correct:
String passwordHash = passwordEncoder.encode(request.password());
userRepository.save(new UserEntity(request.email(), passwordHash, "USER"));
63. Tiny Bug Practice 3
Problem:
if (user.getPasswordHash().equals(request.password())) {
return "login ok";
}
Question:
What is wrong?
Answer:
This compares a stored hash with a raw password and bypasses PasswordEncoder. Use passwordEncoder.matches(rawPassword, storedHash) or better use AuthenticationManager.
Practice Questions and Answers
Question 1
What is UserDetails?
Answer:
UserDetails is Spring Security’s representation of user data needed for authentication, including username, password, authorities, and account status flags.
Question 2
What is UserDetailsService?
Answer:
UserDetailsService loads user data by username and returns UserDetails.
Question 3
What does loadUserByUsername do?
Answer:
It finds a user by username and converts that user into UserDetails.
Question 4
Can username be an email?
Answer:
Yes. In many apps, the email is used as the username.
Question 5
What exception should be thrown when the user is not found?
Answer:
UsernameNotFoundException.
Question 6
What is PasswordEncoder?
Answer:
PasswordEncoder hashes raw passwords and verifies raw passwords against stored hashes.
Question 7
Why should passwords be hashed?
Answer:
Because raw passwords are dangerous if leaked. Hashing reduces damage and prevents storing the actual password.
Question 8
Does PasswordEncoder decode passwords?
Answer:
No. Password encoding is one-way. It verifies with matches.
Question 9
What is AuthenticationManager?
Answer:
AuthenticationManager authenticates an authentication request and returns an authenticated result or throws an exception.
Question 10
What is AuthenticationProvider?
Answer:
AuthenticationProvider performs a specific authentication strategy.
Question 11
What is DaoAuthenticationProvider?
Answer:
DaoAuthenticationProvider is the common username/password provider that uses UserDetailsService and PasswordEncoder.
Question 12
What does DaoAuthenticationProvider use?
Answer:
It uses UserDetailsService to load the user and PasswordEncoder to verify the password.
Question 13
What does roles("ADMIN") create?
Answer:
It creates authority ROLE_ADMIN.
Question 14
What does hasRole("ADMIN") check?
Answer:
It checks for authority ROLE_ADMIN.
Question 15
What is the difference between role and authority?
Answer:
An authority is a permission. A role is usually an authority with the ROLE_ prefix.
Question 16
What is UsernamePasswordAuthenticationToken?
Answer:
It is an Authentication implementation commonly used to carry username/password authentication data.
Question 17
What happens when the password is wrong?
Answer:
Authentication fails, and the response is usually 401.
Question 18
What is @AuthenticationPrincipal?
Answer:
@AuthenticationPrincipal injects the current authenticated principal into a controller method.
Question 19
Why should I not log login request objects directly?
Answer:
Because the object may contain raw passwords or sensitive data.
Question 20
What is the full database login flow?
Answer:
The user sends username/password. Spring Security creates an authentication request. AuthenticationManager delegates to DaoAuthenticationProvider. The provider uses UserDetailsService to load the user from the database and PasswordEncoder to verify the password. If valid, an authenticated Authentication is stored in the SecurityContext.
Final Memory Sentences
UserDetailsis Spring Security’s user representation.UserDetailsServiceloads a user by username.- Username can be an email.
- Throw
UsernameNotFoundExceptionif the user is missing. PasswordEncoderhashes and verifies passwords.- Password hashing is one-way.
- Store password hashes, never raw passwords.
- BCrypt is a common password encoder.
AuthenticationManagerauthenticates credentials.AuthenticationProviderperforms a specific authentication strategy.DaoAuthenticationProviderusesUserDetailsServiceandPasswordEncoder.Authenticationrepresents the login attempt or authenticated user.UsernamePasswordAuthenticationTokencarries username/password data.roles("ADMIN")createsROLE_ADMIN.hasRole("ADMIN")checksROLE_ADMIN.- Do not use
roles("ROLE_ADMIN"). - Use
@AuthenticationPrincipalto inject the current principal. - Never return password hashes.
- Never log raw passwords.
- Encode passwords during registration.
- Use
AuthenticationManagerfor custom login endpoints.