Methods to Create and Handle Custom Exceptions in UserDetailsService of Spring Security

4 min read

1. The Role of Exceptions in UserDetailsService
The UserDetailsService interface has one primary method:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
By default, this method throws UsernameNotFoundException if the user is not found. However, real-world authentication systems often require more granular error handling. For instance:
- Distinguishing between a missing user and an inactive user.
- Providing clear feedback for account lockouts.
The default approach falls short in providing meaningful differentiation in these cases. This is where custom exceptions shine.
2. Creating Custom Exceptions for UserDetailsService
Custom exceptions extend the flexibility of error handling, allowing developers to define precise scenarios. Below, we’ll walk through creating and using custom exceptions.
2.1 Defining Custom Exceptions
Let’s define two custom exceptions:
- UserInactiveException for inactive users.
- UserAccountLockedException for locked accounts.
package com.example.security.exception;
// Custom exception for inactive users
public class UserInactiveException extends RuntimeException {
public UserInactiveException(String message) {
super(message);
}
}
// Custom exception for locked accounts
public class UserAccountLockedException extends RuntimeException {
public UserAccountLockedException(String message) {
super(message);
}
}
By extending RuntimeException, these exceptions are unchecked, which aligns with the standard exception-handling patterns in Spring.
2.2 Modifying UserDetailsService
We implement a custom UserDetailsService to incorporate these exceptions. The service will throw specific exceptions based on the user’s state.
package com.example.security.service;
import com.example.security.exception.UserInactiveException;
import com.example.security.exception.UserAccountLockedException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final Map<String, UserDetails> users = new HashMap<>();
public CustomUserDetailsService() {
users.put("activeUser", User.withUsername("activeUser")
.password("{noop}password")
.roles("USER")
.build());
users.put("lockedUser", User.withUsername("lockedUser")
.password("{noop}password")
.roles("USER")
.accountLocked(true)
.build());
users.put("inactiveUser", User.withUsername("inactiveUser")
.password("{noop}password")
.roles("USER")
.disabled(true)
.build());
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = users.get(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
} else if (!user.isEnabled()) {
throw new UserInactiveException("User account is inactive");
} else if (!user.isAccountNonLocked()) {
throw new UserAccountLockedException("User account is locked");
}
return user;
}
}
In this implementation:
- If a user is not found, UsernameNotFoundException is thrown.
- If the user is inactive, UserInactiveException is thrown.
- If the user’s account is locked, UserAccountLockedException is thrown.
This setup allows for precise error handling and clear communication of issues to the calling code or client.
3. Handling Custom Exceptions in REST APIs
Throwing custom exceptions is only part of the solution. To make these exceptions meaningful to API consumers, you must handle them appropriately. In a Spring application, this is typically done using a global exception handler.
3.1 Defining an Exception Handler
Here’s how to create a global exception handler:
package com.example.security.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserInactiveException.class)
public ResponseEntity<String> handleUserInactiveException(UserInactiveException ex) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ex.getMessage());
}
@ExceptionHandler(UserAccountLockedException.class)
public ResponseEntity<String> handleUserAccountLockedException(UserAccountLockedException ex) {
return ResponseEntity.status(HttpStatus.LOCKED).body(ex.getMessage());
}
@ExceptionHandler(UsernameNotFoundException.class)
public ResponseEntity<String> handleUsernameNotFoundException(UsernameNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
}
This handler:
- Maps each exception to an HTTP status code (403 FORBIDDEN, 423 LOCKED, 404 NOT FOUND).
- Returns a meaningful error message to the client.
3.2 Testing the Custom Exceptions
You can test this implementation using tools like Postman:
- Attempt to authenticate with inactiveUser – the response will have a 403 FORBIDDEN status.
- Authenticate with lockedUser – the response will have a 423 LOCKED status.
- Use an invalid username – the response will return 404 NOT FOUND.
4. Benefits of Custom Exceptions
Enhanced Debugging
Custom exceptions provide context-specific information, making it easier to debug issues.
Improved Client Communication
With tailored exceptions, clients receive precise error messages, enhancing the API’s usability.
Scalability and Flexibility
As the application evolves, additional custom exceptions can be seamlessly integrated to handle new scenarios.
5. Conclusion
Custom exceptions in Spring Security’s UserDetailsService enable precise and robust error handling tailored to your application’s needs. By defining and handling these exceptions, you not only improve debugging and client communication but also pave the way for a more maintainable and scalable application.
If you have any questions or need assistance implementing custom exceptions, feel free to comment below. Let’s discuss and solve your challenges together!
Read more at : Methods to Create and Handle Custom Exceptions in UserDetailsService of Spring Security
0
Subscribe to my newsletter
Read articles from Tuanhdotnet directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Tuanhdotnet
Tuanhdotnet
I am Tuanh.net. As of 2024, I have accumulated 8 years of experience in backend programming. I am delighted to connect and share my knowledge with everyone.