3

I have a Spring boot (tomcat) REST controller declared as produces = "text/plain". My application uses spring security. If I send a request that will result in a 403 then the default spring boot json error handler will try to return a Content-Type of application/json.

This results in the client actually getting a 406 error org.springframework.web.HttpMediaTypeNotAcceptableException because the client specified Accept: text/plain in the request.

The obvious answer is to ask the client to specify two Accept headers with text/plain and application/json. Doesn't work, you still get the 406. Doesn't work either with a single, multi-valued header separated by commas.

In all cases the 403 survives all the way to StandardHostValve.status() along with the two Accept headers in the request but fails somewhere in the default error page forwarder.

Any ideas?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Andy Brown
  • 11,766
  • 2
  • 42
  • 61
  • If your client includes only `application/json` in the accepts request header, does it work? – Taylor Sep 16 '16 at 12:50
  • It does when the method itself produces `application/json`. If it produces anything else then no, 406 is the result - and that's generated before the 403. – Andy Brown Sep 16 '16 at 13:46
  • Hi @AndyBrown, I am facing exactly this same issue. Do you remember how did you solved it? – y.luis.rojo Mar 07 '18 at 15:14
  • @y.luis We added both possible values to the produces field. i.e. `produces = {APPLICATION_JSON_UTF8_VALUE, TEXT_PLAIN_VALUE}`. Does that work for you? – Andy Brown Mar 07 '18 at 15:37
  • Thanks for your quick reply @AndyBrown. I went for another solution which I describe as an answer in case it works for someone else. – y.luis.rojo Mar 07 '18 at 15:57

1 Answers1

0

What I did, based on this solution, was to add a custom AccessDeniedHandler which sets the response status and writes to it:

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

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

        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.getWriter().write(accessDeniedException.getMessage());
    }
}

and configure it on the WebSecurityConfigurerAdapter:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApiWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    //...

    @Autowired
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic()
            .authenticationEntryPoint(authenticationEntryPoint).and().exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);
    }
}

Thus returning Content-Type: text/plain independently on the Accept request header.

y.luis.rojo
  • 1,794
  • 4
  • 22
  • 41