5
votes

I'm trying to make an ajax call to my Spring controller/action with POST method, and return an object from the server with @ResponseBody. The strange situation is that it stop working after add spring security layer, everything was working fine before. I will try to explain my moves to solve the problem and then show you the code/captures/etc.

1. After some research I found some answers telling the problem might be related with csrf mechanism, so I disabled it and still have the issue. (spring-security.xml bellow)

2. I've made a wireshark capture to check the request/response. My ajax request is OK, my controller declaration is OK, but I don't understand why, the 405 response indicates > Allow: GET (capture bellow)

3. I've tried to access my controller action through the browser (i.e., make an GET request), and I get the error HTTP Status 405 - Request method 'GET' not supported!

4. I've tried to change the RequestMapping(method...) to RequestMethod.GET and the request arrives to the controller and works fine, but I don't want it to work on GET method, I want a POST request.

5. Changed the RequestMapping(consumes, produces, headers) to accept all kind of data, but still 405...

This is driving me crazy! I post my files bellow, so you can check it guys, any tip will be appreciated. Thanks! (IMPORTANT NOTE: this is my despair configuration)

spring-security.xml

<beans:beans 
     xmlns...(all needed declarations)>

<http pattern="/js/**" security="none" />
<http pattern="/css/**" security="none" />

<!-- enable use-expressions -->
<http auto-config="true" >
    <access-denied-handler error-page="/403" />
    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/login" access="isAnonymous()" />
    <intercept-url pattern="/403" access="permitAll" />
    <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />

    <form-login  login-page="/login"
                 username-parameter="email"
                 password-parameter="password"
                 authentication-failure-url="/login?failed" />

    <!--
    <csrf/>
    -->
</http>

 ..... (authentication)  

AdminController.java

@Controller
@RequestMapping("/admin**")
public class AdminController {

    ... (all my autowired beans)

    @RequestMapping(
        value = "/events/loadEvents",
        method = RequestMethod.POST,
        consumes = MediaType.ALL_VALUE,
        produces = MediaType.ALL_VALUE,
        headers = "Accept=*/*")
    @ResponseBody
    public Event loadEvents(@RequestParam("parentId") long parentId) {
        ... (my logic)
        return event;
    }
}

Request (wireshark capture) HTTP Request (link smudged because I've used a simplified on my question)

Response (wireshark capture) enter image description here

EDIT jquery ajax call code

$.ajax({
    type: 'POST',
    cache: false,
    url: /admin/events/loadEvents,
    data: { parentId: 1 },
    dataType = 'json',
    contentType = 'application/json',

    ...
});
3
I can't see you sending any credentials? How is your 403 implemented?holmis83
I reach this area of the app only after authenticated, i.e., the first step is to authenticate and then the session authentication is managed by Spring Security. And as I mentioned, if I change the jquery call and controller action method to GET, it works fine. The problem is, why I get 405 with POST if everything is well configured.rmpt
can anybody throw light on why it has to give 405 instead of any other status? 405 should be shown when the URI doesn't support the given HTTP method right?pinkpanther

3 Answers

12
votes

After many hours of research and tests, I finally got it, ant it was a (very very) stupid situation. So, in my question I said

so I disabled it (csrf on spring-security.xml) and still have the issue.

No, I didn't disabled it. I was trying to disable it doing

<!--
<csrf/>
-->

But I should be doing:

<csrf disabled="true"/>

Commenting csrf tag does NOT disable csrf, this is because csrf is enabled by default! After find the problem is really easy to say that is a stupid mistake, but as I added csrf tag to enable it, I thought that commenting it would disable it. Find the answer on Spring Documentation

Now, back into my problem. To fix the 405 error message in a POST AJAX call WITH CSRF ENABLED, it was really easy. I keep the csrf parameters in JS variables like this:

<script type="text/javascript">
    var csrfParameter = '${_csrf.parameterName}';
    var csrfToken = '${_csrf.token}';
</script>

and then my ajax call looks like this:

var jsonParams = {};
jsonParams['parentId'] = 1;
jsonParams[csrfParameter] = csrfToken;
$.ajax({
    type: 'POST',
    cache: false,
    url: /admin/events/loadEvents,
    data: jsonParams,
    dataType = 'json',
    contentType = 'application/json',

    ...
});

Working like a charme. Hope that helps someone in the future.

0
votes

$.ajaxSetup({
    dataType: "json",
    beforeSend: function(xhr, settings){
        var csrftoken = $.cookie('CSRF-TOKEN');
        xhr.setRequestHeader("X-CSRF-TOKEN", csrftoken);
    },
});
0
votes

In my case, with same problem, helps this:

  • add taglib:
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
  • add in jsp body:
<sec:csrfMetaTags />
  • add in ajax
headers: {"X-CSRF-TOKEN": $("meta[name='_csrf']").attr("content")}

P.S. Thanks to Illya Shulgin, cool ansver, now it here.