Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring ControllerAdvice and authentication/authorization exception handling

In my Spring Boot appliation I have a following web security config:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off   
        http
            .headers().frameOptions().disable()
            .and()
                .antMatcher("/**").authorizeRequests()
                .antMatchers("/actuator/health").permitAll()
                .antMatchers("/actuator/**").hasAuthority(Authority.Type.ROLE_ADMIN.getName())
                .antMatchers("/login/**").permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .loginProcessingUrl("/login")
                    .failureUrl("/login?error").permitAll()
            .and()
                .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/login?logout")
            .and()
                .csrf().csrfTokenRepository(csrfTokenRepository()).ignoringAntMatchers("/login/**")
            .and()
                .addFilterBefore(corsFilter, ChannelProcessingFilter.class)
                .addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class)
                .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class)
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());
        // @formatter:on
    }

....

}

and ControllerAdvice:

@ControllerAdvice
public class GlobalControllerExceptionHandler  {

    private static final String ERROR = "error";

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Map<String, ErrorResponse> handleException(Exception exception) {
        return createResponseError(exception);
    }

    private Map<String, ErrorResponse> createResponseError(Exception exception) {
        final Map<String, ErrorResponse> responseError = new HashMap<String, ErrorResponse>();
        responseError.put(ERROR, new ErrorResponse(exception.getMessage()));
        return responseError;
    }

}

Right now, when I'm trying to access my secure API urls by anonymous user, I'm receiving a following error:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri May 06 22:59:05 EEST 2016
There was an unexpected error (type=Forbidden, status=403).
Access Denied

Instead of this page, I need to handle such kind of errors in my ControllerAdvice (in order to return JSON error response) which is working fine for all other exceptions but only for authenticated users.

How to handle this Authentication/Authorization error by my GlobalControllerExceptionHandler?

like image 807
alexanoid Avatar asked Dec 05 '22 18:12

alexanoid


1 Answers

ControllerAdvices are designed to assist Controller classes and ExceptionHandlers are for handling exceptions thrown by Controllers. AuthenticationException and AccessDeniedExceptions are usually thrown by AbstractSecurityInterceptor of Spring Security. So, i'm guessing that you won't be able to catch those AuthenticationExceptions using ControllerAdvices, since ExceptionTranslationFilter would already catch them and convert them to appropriate HTTP responses.

The better approach is to use exceptionHandling in your WebSecurityConfigurerAdapter. Using that, you can configure a AuthenticationEntryPoint and AccessDeniedHandler. Here, i'm returning a 403 Forbidden for access denied cases and a 401 Unauthorized for missing authentication tokens case:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception { 
        http.exceptionHandling()
            .accessDeniedHandler((request, response, accessDeniedException) -> {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
            })
            .authenticationEntryPoint((request, response, authException) -> {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            });
    }

}

Using exceptionHandling you can configure ExceptionTranslationFilter to use accessDeniedHandler for handling AccessDeniedExceptions and authenticationEntryPoint for AuthenticationExceptions. Take a peek at ExceptionTranslationFilter to gain more insight about the process.

If you don't like:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri May 06 22:59:05 EEST 2016
There was an unexpected error (type=Forbidden, status=403).
Access Denied

And want to customize it, you should provide an implementation for ErrorController and return a Map<String, ErrorResponse> for errors.

like image 96
Ali Dehghani Avatar answered Dec 09 '22 13:12

Ali Dehghani