2

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.

Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148

2 Answers2

0

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.

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98
-1

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);
  }
Nicholas Smith
  • 11,642
  • 6
  • 37
  • 55
  • Are there some extra conditions which need to be met? The `AccessDeniedHandler` doesn't get invoked. It is declared as a `@Component` and autowired into my configuration via DI. The `AccessDeniedException ` I throw in the filter is from the same package like the one from `handle()`. – Sasha Shpota Dec 20 '17 at 14:49
  • How are you creating the handler? We do it via constructor dependency injection. – Nicholas Smith Dec 20 '17 at 16:48
  • I'm doing it via constructor injection as well. But I guess that is not the issue. May be you have some extraconfig which I don't. – Sasha Shpota Dec 20 '17 at 16:49
  • Possibly, I remember this being some what tricky to set up (i.e. a day of cursing). I'll try create a minimal example that gets it working. – Nicholas Smith Dec 20 '17 at 20:16