1

I am trying to use @ControllerAdvice annotation to return custom JSON responses in spring boot. Below is my code. defaultExceptionHandler gets called fine if there is an error in the application or spring boot service throws an exception. But handleAuthenticationException never gets called even if I enter bad credentials. In this case, default spring-boot response is returned. This service is security confi is setup to use basic auth.

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    @ExceptionHandler({AuthenticationException.class})
    public ServiceError handleAuthenticationException(final Exception ex) {
        //LOGGER.error(ex.toString(), ex);
        return new ServiceError(ServiceErrorCode.REQUEST_INVALID, ex.getMessage());
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ServiceError defaultExceptionHandler(final Exception ex) {
        //LOGGER.error(ex.toString(), ex);
        return new ServiceError(ServiceErrorCode.SERVICE_ERROR, ex.getMessage());
    }
}

Default response which is getting return instead of custom response:

   {
"timestamp": 16654436345,
"status": 401,
"error": "Unauthorized",
"message": "Bad credentials",
"path": "/mycontroller"}

Securtiy config class:

    @Configuration
public static class ServiceWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.
            csrf().
            disable().
            exceptionHandling().
        and().
            headers().
            frameOptions().
            disable().
        and().
            sessionManagement().
            sessionCreationPolicy(SessionCreationPolicy.STATELESS).
        and().
            authorizeRequests().
            antMatchers("/service/**").authenticated().
            antMatchers("/health").permitAll().
            antMatchers("/metrics").permitAll().
            antMatchers("/info").hasAuthority(AuthoritiesConstants.ADMIN).
            anyRequest().authenticated().and().httpBasic();
    }
}
PUG
  • 4,301
  • 13
  • 73
  • 115
  • Depending on how you set up your security config, which you didn't show, the 401 might be being triggered before the MVC dispatch happens. Turn up your logging to `DEBUG` and post the results. – chrylis -cautiouslyoptimistic- Dec 14 '20 at 01:44
  • Thank chrylis for suggestion. posting security config – PUG Dec 14 '20 at 01:47
  • _It appears_ the security code is sending back a `HttpServletResponse.SC_UNAUTHORIZED` response - given the 401. Therefore, the expected exception is never thrown. – Randy Casburn Dec 14 '20 at 01:59

1 Answers1

1

Spring MVC is based on servlet, and spring security is based on filter, filter is before servlet, so the exception handler in your controller will not be executed, because it's already failed 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

http.exceptionHandling().authenticationEntryPoint(myEntryPoint());

AuthenticationException and AccessDeniedException have already been handled by ExceptionTranslationFilter. You just need to inject AuthenticationEntryPoint and AccessDeniedHandler(which handle AccessDeniedException)

chaoluo
  • 2,596
  • 1
  • 17
  • 29