I am implementing custom 'AuthenticationProvider'. If not authenticated I am throwing exception inside 'authenticate' function as shown below.
public class DelegatingLdapAuthenticationProvider implements AuthenticationProvider {
private ActiveDirectoryLdapAuthenticationProvider primaryProvider;
private List<ActiveDirectoryLdapAuthenticationProvider> secondaryProviders = new ArrayList<>();
public DelegatingLdapAuthenticationProvider() {
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Authentication result = null;
AuthenticationException exception = null;
try {
result = primaryProvider.authenticate(authentication);
} catch (AuthenticationException e) {
exception = e;
for (ActiveDirectoryLdapAuthenticationProvider secondaryProvider : secondaryProviders) {
try {
result = secondaryProvider.authenticate(authentication);
if (result.isAuthenticated()) {
break;
}
} catch (AuthenticationException e1) {
exception = e;
}
}
}
if (result == null || !result.isAuthenticated()) {
throw exception;
}
return result;
}
I have global exception handler as shown below.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({NoPermissionException.class})
@ResponseBody
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public Map<String, String> noPermission(NoPermissionException e) {
return createErrorResponse(e, "Don't have permissions");
}
@ExceptionHandler({Exception.class})
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, String> exceptionInProcessing(Exception e) {
return createErrorResponse(e, "Unable to process. Unknown error occurred: " + e.getMessage());
}
private Map<String, String> createErrorResponse(Exception e, String errorMessage) {
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("message", errorMessage);
errorResponse.put("reason", e.toString());
return errorResponse;
}
}
When exception is thrown inside the 'authenticate' function, global exception handler is not being called. For all the other exceptions it is being called. I want to catch the exception inside global exception handler and return custom error message. How can I do that? Any help appreciated. Thanks in advance.
The Authentication Manager is only a interface and actual implementation of the authenticate method is provided by the ProviderManager. The ProviderManager has a list of AuthenticationProviders. From it's authenticate method it calls the authenticate method of the appropriate AuthenticateProvider.
To handle these exceptions at a global level via @ExceptionHandler and @ControllerAdvice, we need a custom implementation of AuthenticationEntryPoint. AuthenticationEntryPoint is used to send an HTTP response that requests credentials from a client.
Authentication providers are responsible to perform a specific authentication. Spring security provides several AuthenticationProvider . Remember these AuthenticationProviders can't execute directly, but spring security uses ProviderManager class which delegates to a list of configured authentication providers.
To plug in the new implementation of the AuthenticationProvider, override the configure(AuthenticationManagerBuilder auth) method of the WebSecurityConfigurerAdapter class in the configuration class. To register the multiple AuthenticationProvider implementations, we can invoke the auth.
The GlobalExceptionHandler is for controller exception handler, but the AuthenticationProvider is still in filter, if you want to handler the AuthenticationException, you need to handle it to implement AuthenticationEntryPoint and override the commence method.
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException
AuthenticationException and AccessDeniedException have already been handled by ExceptionTranslationFilter. You just need to inject AuthenticationEntryPoint and AccessDeniedHandler(which handle AccessDeniedException)
Or you can catch these exception in filter and then handle it in filer, like AuthenticationFailureHandler in AbstractAuthenticationProcessingFilter
To complement the @chaoluo answer:
AuthenticationEntryPoint interface and resolve the exception by HandlerExceptionResolver:@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint, AccessDeniedHandler {
@Autowired
private HandlerExceptionResolver resolver;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
resolver.resolveException(request, response, null, exception);
}
}
RestAuthenticationEntryPoint into your WebSecurityConfigurerAdapter implementation and use it as the authenticationEntryPoint:@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
HandlerExceptionResolver, we can use the typical Spring Web error handling using @ControllerAdvice and @ExceptionHandler annotations:@RestControllerAdvice
public abstract class ErrorsControllerAdvice {
@ExceptionHandler
public ResponseEntity<?> handleException(Throwable exception, WebRequest webRequest, Locale locale) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
}
}
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