Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle Security exceptions in Spring Boot Resource Server

How can I get my custom ResponseEntityExceptionHandler or OAuth2ExceptionRenderer to handle Exceptions raised by Spring security on a pure resource server?

We implemented a

@ControllerAdvice
@RestController
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

so whenever there is an error on the resource server we want it to answer with

{
  "message": "...",
  "type": "...",
  "status": 400
}

The resource server uses the application.properties setting:

security.oauth2.resource.userInfoUri: http://localhost:9999/auth/user

to authenticate and authorize a request against our auth server.

However any spring security error will always bypass our exception handler at

    @ExceptionHandler(InvalidTokenException.class)
    public ResponseEntity<Map<String, Object>> handleInvalidTokenException(InvalidTokenException e) {
        return createErrorResponseAndLog(e, 401);
    }

and produce either

{
  "timestamp": "2016-12-14T10:40:34.122Z",
  "status": 403,
  "error": "Forbidden",
  "message": "Access Denied",
  "path": "/api/templates/585004226f793042a094d3a9/schema"
}

or

{
  "error": "invalid_token",
  "error_description": "5d7e4ab5-4a88-4571-b4a4-042bce0a076b"
}

So how do I configure the security exception handling for a resource server? All I ever find are examples on how to customize the Auth Server by implementing a custom OAuth2ExceptionRenderer. But I can't find where to wire this to the resource server's security chain.

Our only configuration/setup is this:

@SpringBootApplication
@Configuration
@ComponentScan(basePackages = {"our.packages"})
@EnableAutoConfiguration
@EnableResourceServer
like image 755
Pete Avatar asked Dec 14 '16 10:12

Pete


People also ask

How do you handle security exceptions in Spring?

Spring security exceptions can be directly handled by adding custom filters and constructing the response body. To handle these exceptions at a global level via @ExceptionHandler and @ControllerAdvice, we need a custom implementation of AuthenticationEntryPoint.

Can we handle exceptions in Spring boot?

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.


3 Answers

As noted in previous comments the request is rejected by the security framework before it reaches the MVC layer so @ControllerAdvice is not an option here.

There are 3 interfaces in the Spring Security framework that may be of interest here:

  • org.springframework.security.web.authentication.AuthenticationSuccessHandler
  • org.springframework.security.web.authentication.AuthenticationFailureHandler
  • org.springframework.security.web.access.AccessDeniedHandler

You can create implementations of each of these Interfaces in order to customize the response sent for various events: successful login, failed login, attempt to access protected resource with insufficient permissions.

The following would return a JSON response on unsuccessful login attempt:

@Component
public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler
{
  @Override
  public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
      AuthenticationException ex) throws IOException, ServletException
  {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    
    Map<String, Object> data = new HashMap<>();
    data.put("timestamp", new Date());
    data.put("status",HttpStatus.FORBIDDEN.value());
    data.put("message", "Access Denied");
    data.put("path", request.getRequestURL().toString());
    
    OutputStream out = response.getOutputStream();
    com.fasterxml.jackson.databind.ObjectMapper mapper = new ObjectMapper();
    mapper.writeValue(out, data);
    out.flush();
  }
}

You also need to register your implementation(s) with the Security framework. In Java config this looks like the below:

@Configuration
@EnableWebSecurity
@ComponentScan("...")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
  @Override
  public void configure(HttpSecurity http) throws Exception
  {
    http
       .addFilterBefore(corsFilter(), ChannelProcessingFilter.class)
       .logout()
       .deleteCookies("JESSIONID")
       .logoutUrl("/api/logout")
       .logoutSuccessHandler(logoutSuccessHandler())
       .and()
       .formLogin()
       .loginPage("/login")
       .loginProcessingUrl("/api/login")
       .failureHandler(authenticationFailureHandler())
       .successHandler(authenticationSuccessHandler())
       .and()
       .csrf()
       .disable()
       .exceptionHandling()
       .authenticationEntryPoint(authenticationEntryPoint())
       .accessDeniedHandler(accessDeniedHandler());
  }

  /**
   * @return Custom {@link AuthenticationFailureHandler} to send suitable response to REST clients in the event of a
   *         failed authentication attempt.
   */
  @Bean
  public AuthenticationFailureHandler authenticationFailureHandler()
  {
    return new RestAuthenticationFailureHandler();
  }

  /**
   * @return Custom {@link AuthenticationSuccessHandler} to send suitable response to REST clients in the event of a
   *         successful authentication attempt.
   */
  @Bean
  public AuthenticationSuccessHandler authenticationSuccessHandler()
  {
    return new RestAuthenticationSuccessHandler();
  }

  /**
   * @return Custom {@link AccessDeniedHandler} to send suitable response to REST clients in the event of an attempt to
   *         access resources to which the user has insufficient privileges.
   */
  @Bean
  public AccessDeniedHandler accessDeniedHandler()
  {
    return new RestAccessDeniedHandler();
  }
}
like image 183
Alan Hay Avatar answered Oct 22 '22 20:10

Alan Hay


In case if you're using @EnableResourceServer, you may also find convenient to extend ResourceServerConfigurerAdapter instead of WebSecurityConfigurerAdapter in your @Configuration class. By doing this, you may simply register a custom AuthenticationEntryPoint by overriding configure(ResourceServerSecurityConfigurer resources) and using resources.authenticationEntryPoint(customAuthEntryPoint()) inside the method.

Something like this:

@Configuration
@EnableResourceServer
public class CommonSecurityConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.authenticationEntryPoint(customAuthEntryPoint());
    }

    @Bean
    public AuthenticationEntryPoint customAuthEntryPoint(){
        return new AuthFailureHandler();
    }
}

There's also a nice OAuth2AuthenticationEntryPoint that can be extended (since it's not final) and partially re-used while implementing a custom AuthenticationEntryPoint. In particular, it adds "WWW-Authenticate" headers with error-related details.

like image 43
Vladimir Salin Avatar answered Oct 22 '22 20:10

Vladimir Salin


You are not able to make use of Spring MVC Exception handler annotations such as @ControllerAdvice because spring security filters kicks in much before Spring MVC.

like image 4
so-random-dude Avatar answered Oct 22 '22 19:10

so-random-dude