5

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',

    ...
});
rmpt
  • 557
  • 1
  • 3
  • 20
  • I can't see you sending any credentials? How is your 403 implemented? – holmis83 Dec 15 '15 at 10:36
  • 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 Dec 15 '15 at 12:03
  • 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 Feb 29 '16 at 14:08

3 Answers3

12

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.

rmpt
  • 557
  • 1
  • 3
  • 20
  • Thanks it helped me a lot – Kamaraju May 18 '17 at 00:41
  • Thanks. This helped me. I want your opinion on the following - I have few RestControllers which return JSON response and few return views. If I enable csrf, when I do ajax call to rest controllers, if fails with 405 error. What should I be doing? Disable csrf? or send csrf token in ajax calls? Which one is recommended? – Kuldeep Singh Nov 21 '19 at 13:21
  • you should send your csrf token in every call. just for safety reasons. if you don't mind with security issues, then you can just disable csrf – rmpt Nov 21 '19 at 17:00
0

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

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.

Андрей
  • 1
  • 1
  • 1