Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching exception thrown in AuthenticationProvider

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.

like image 323
chom Avatar asked Dec 22 '16 00:12

chom


People also ask

What is difference between AuthenticationManager and AuthenticationProvider?

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.

How do you handle authentication exception?

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.

What is AuthenticationProvider in Spring Security?

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.

How do I bypass authentication provider?

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.


2 Answers

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

like image 193
chaoluo Avatar answered Oct 27 '22 22:10

chaoluo


To complement the @chaoluo answer:

  1. Implement the 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);
    }

}
  1. Inject the 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);
    }
}
  1. Now because we resolved the exception by 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);
    }
}

like image 35
youhans Avatar answered Oct 27 '22 22:10

youhans