I have created a custom AuthenticationProvider
to perform custom security checks. I have also created custom exceptions that inherit from AccountStatusException
to notify user status problems such as when the user has not verified his account for an specific period of time.My UserDetails
is also acustom implementation.
Here is the code for the security checks I perform. Code that is irrelevant to the case has been omitted.
public class SsoAuthenticationProvider implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = (String) authentication.getPrincipal();
User user = null;
if (username != null) {
user = getUserRepository().findByUserName(username);
if (user != null) {
if (user.getEnabled() != 0) {
if ((user.getUserDetail().getConfirmed() != 0)
|| ((new Date().getTime() - user.getUserDetail().getRequestDate().getTime()) / (1000 * 60 * 60 * 24)) <= getUnconfirmedDays()) {
if (getPasswordEncoder().isPasswordValid(user.getPassword(),
(String) authentication.getCredentials(), user)) {
user.authenticated = true;
user.getAuthorities();
}
} else {
throw new UserNotConfirmedAndTimeExceeded(
"User has not been cofirmed in the established time period");
}
} else {
throw new DisabledException("User is disabled");
}
} else {
throw new BadCredentialsException("User or password incorrect");
}
} else {
throw new AuthenticationCredentialsNotFoundException("No credentials found in context");
}
return user;
}
}
The SsoAuthenticationProvider
checks:
The problem is that not all of these exceptions are thrown up the stack up to the controller so it seems impossible to inform the user about the login problem.
Using UserDetails
methods such as isEnabled()
(and similar) is not a possibility as the semantics of our different user account statuses are completely different.
Is this the right approach to build custom security with custom exceptions? Should i implement sth else to make this work?
The @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client. Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown.
Spring MVC provides exception handling for your web application to make sure you are sending your own exception page instead of the server-generated exception to the user. The @ExceptionHandler annotation is used to detect certain runtime exceptions and send responses according to the exception.
Spring MVC Framework provides following ways to help us achieving robust exception handling. Controller Based - We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument.
Exception Handling in Spring Boot helps to deal with errors and exceptions present in APIs so as to deliver a robust enterprise application. This article covers various ways in which exceptions can be handled in a Spring Boot Project. Let's do the initial setup to explore each approach in more depth.
To close the previously asked question let me explain what we did. As I commented to previous responses, using provided methods in UserDetails objectis not feasible as you cannot capture all the login failure semantics with the given methods. In our case these semantics are still very limited but in other cases it could indfinitely extend over time to express different user situations. The exception approach was finally the best one. The final code looks like this
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username=(String)authentication.getPrincipal();
User user=null;
if(username!=null){
user=getUserRepository().findByUserName(username);
if(user!=null){
if(user.getEnabled()!=0){
if((user.getUserDetail().getConfirmed()!=0)||((new Date().getTime()-user.getUserDetail().getRequestDate().getTime())/(1000 * 60 * 60 * 24))<=getUnconfirmedDays()){
if(getPasswordEncoder().isPasswordValid(user.getPassword(), (String)authentication.getCredentials(), user)){
user.authenticated=true;
user.getAuthorities();
} else {
throw new BadCredentialsException("Password incorrect");
}
}else{
throw new UserNotConfirmedAndTimeExceeded("User has not been cofirmed in the established time period");
}
}else{
throw new DisabledException("User is disabled");
}
}else{
throw new BadCredentialsException("User does not exist");
}
}else{
throw new AuthenticationCredentialsNotFoundException("No credentials found in context");
}
return user;
}
All exceptions are part of the spring security exception stack. This is, those custom exceptions inherit from some existing exception. Then, in your security controller you should check for security exceptions and treat them as desired. For example redirecting to different pages.
Hope this helps!
i think its better to use other method/properties of user detail object for this purpose. like
isAccountNonExpired()
isAccountNonLocked()
isEnabled()
and if you want to display custom error message then use message properties as explained in this article
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With