borq
borq

Reputation: 681

405 vs 403 returned from Spring Controllers when using @PreAuthorize

We recently started using the @PreAuthorize annotation with our REST endpoints. It works great, however, I did have a question regarding the HTTP code returned when issuing a GET vs. a POST or PUT. It appears that when a user is not authorized to access the controller's REST endpoint that the HTTP status returned is different for GET and PUT/POST.

So for example, if I have an endpoint that is a GET and has a @PreAuthorize annotation and the user doesn't have access, a 403 Forbidden is returned. This is what I expect.

If the same annotation is then placed on a controller method that is a POST or a PUT, the HTTP response is 405 Method Not Allowed (note that when properly authorized the POST/PUT method returns 200 as expected).

When stepping through the code you can see that the underlying security filter returns a 403, but in the POST/PUT scenario the status code is dropped/ignored and replaced with a 405, much like it does when a NullPointerExcpetion occurs in your controller code.

Is this the expected behavior or should a 403 Forbidden always be returned for users who do not have access to an end point?

Upvotes: 11

Views: 4462

Answers (3)

Florian
Florian

Reputation: 33

I solved this issue by allowing additional HTTP methods in my error controller, instead of allowing only GET requests:

public class MyErrorController implements ErrorController {
    @RequestMapping(method = {
            RequestMethod.GET,
            RequestMethod.HEAD,
            RequestMethod.POST,
            RequestMethod.PUT,
            RequestMethod.DELETE,
            RequestMethod.OPTIONS,
            RequestMethod.TRACE,
            RequestMethod.PATCH})
    public String displayError(){
        // display error
    }
}

My explanation:

  1. POST method is allowed for the requested resource, but SpringSecurity rejects the request due to invalid authentication or authorization.
  2. SpringSecurity internally redirects to the error controller to show the error page for HTTP 403. The HTTP method from the initial request is used here, which is POST.
  3. The error controller only allows GET requests, which is why HTTP 405 is returned.

Upvotes: 0

mleister
mleister

Reputation: 2585

For me the problem was inside the SecurityConfiguration.

By removing the line .and().exceptionHandling().accessDeniedPage("/access-denied") i got 403 Forbidden instead of 405 Method not allowed which is most probably what you would expect.

Upvotes: 2

Farooq Khan
Farooq Khan

Reputation: 588

It is possible that somewhere in between the request is being internally redirected and is ending up on a endpoint that is expecting a different HTTP request type and hence you are getting a HTTP 405. I have seen this has the most common reason in case of authentications

Upvotes: 0

Related Questions