简体   繁体   中英

Spring Security: mapping of exceptions thrown inside security filter

I have custom security filter which serves as additional authorization step.

The filter checks if a user can be authorized and throws an exception if the user is not supposed to access the resource.

The problem is that if I throw an exception from the filter - it doesn't get mapped to correct status code (in my case I need HTTP 403).

I can't use @ControllerAdvice and @ExceptionHandler because security filters work before controller handling .

I thought may be I'm doing it wrong and I shouldn't throw an exception, or I should throw a very specific one.

Q: Is there any way to automatically map exceptions from filters to proper status codes? Or is there a way to implement the filter without exceptions?

Note: I also read this post , but from debug I see that my filter chain doesn't contain ExceptionTranslationFilter .

Or is there a way to implement the filter without exceptions?

Obviously, you can directly write to response and return from the very point you catch an authentication or authorization failure. Throwing exceptions then globally handling it seemed too much unnecessary overwork for me since I had only one JWT authentication filter implementing - AbstractAuthenticationProcessingFilter

Whenever an authentication condition is not met , like JWT token parsing issue, missing token , malformed token etc , I simply log error , set HttpStatus and return null from attemptAuthentication method.

This way my whole logic is encapsulated in single class. I also had to send 401 for all cases but error messages were different and that was handled by a simple utility method.

I don't find any fault in throwing and then handling exceptions if you have to do that from many places in your application and in that case handler could be specified as in Nicholas Smith's answer.

The Spring recommended way is to have a @Component that implements AccessDeniedHandler and then in your class that extends WebSecurityConfigurerAdapter register it to the .accessDeniedHandler() in the configure(HttpSecurity ...) method. That's for 403 , if you want 401 errors to be wrapped as well then you need to extend Http401AuthenticationEntryPoint . I'll focus on 403 cases below.

Your WebSecurityConfigurerAdapter

@Override
  protected void configure(final HttpSecurity http) throws Exception {
    ...
    http
        .csrf().disable()
        .exceptionHandling().authenticationEntryPoint(<YOUR Http401AuthenticationEntryPoint>)
        .and()
        .exceptionHandling().accessDeniedHandler(<YOUR ACCESS DENIED HANDLER>);
    ...
  }

Your AccessDeniedHandler

@Override
  public void handle(HttpServletRequest request,
                     HttpServletResponse response,
                     AccessDeniedException accessDeniedException) throws IOException, ServletException {

    response.setHeader("Content-Type", "application/hal+json;charset=UTF-8");
    response.getOutputStream()
        .print(objectMapper
            .writeValueAsString("Access Denied");
    response.setStatus(403);
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM