Ajax POST води до 405 (методът не е разрешен) - Spring MVC

Опитвам се да направя ajax повикване към моя Spring контролер/действие с POST метод и да върна обект от сървъра с @ResponseBody. Странната ситуация е, че спира да работи след добавяне на пружинен защитен слой, преди всичко работеше добре. Ще се опитам да обясня ходовете си за решаване на проблема и след това да ви покажа кода/улавянията/и т.н.

1. След известно проучване намерих някои отговори, които казват, че проблемът може да е свързан с механизма на csrf, така че го деактивирах и все още имам проблема. (spring-security.xml по-долу)

2. Направих wireshark capture, за да проверя заявката/отговора. Моята ajax заявка е наред, декларацията на контролера ми е наред, но не разбирам защо, отговорът 405 показва > Разрешаване: GET (снимка по-долу)

3. Опитах се да осъществя достъп до моето действие на контролера през браузъра (т.е. да направя GET заявка) и получавам грешка HTTP Статус 405 - Методът на заявка „GET“ не се поддържа!

4. Опитах се да променя RequestMapping(method...) на RequestMethod.GET и заявката пристига до контролера и работи добре, но не искам да работи с GET метода, искам POST заявка.

5. Променено е RequestMapping(consumes, produces, headers), за да приема всички видове данни, но все още 405...

Това ме подлудява! Публикувам файловете си по-долу, за да можете да ги проверите, момчета, всеки съвет ще бъде оценен. Благодаря! (ВАЖНА ЗАБЕЛЕЖКА: това е моята конфигурация на отчаяние)

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;
    }
}

Заявка (улавяне на wireshark) HTTP заявка (връзката е размазана, защото съм използвал опростена на въпроса си)

Отговор (улавяне на wireshark) въведете описание на изображението тук

РЕДАКТИРАНЕ jquery ajax код за извикване

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

    ...
});

person rmpt    schedule 13.12.2015    source източник
comment
Не виждам да изпращате идентификационни данни? Как е внедрен вашият 403?   -  person holmis83    schedule 15.12.2015
comment
Стигам до тази област на приложението само след удостоверяване, т.е. първата стъпка е удостоверяване и след това удостоверяването на сесията се управлява от Spring Security. И както споменах, ако променя извикването на jquery и метода на действие на контролера на GET, работи добре. Проблемът е защо получавам 405 с POST, ако всичко е добре конфигурирано.   -  person rmpt    schedule 15.12.2015
comment
може ли някой да хвърли светлина защо трябва да дава 405 вместо друг статус? 405 трябва да се показва, когато URI не поддържа дадения HTTP метод, нали?   -  person pinkpanther    schedule 29.02.2016


Отговори (2)


След много часове проучвания и тестове, най-накрая го получих, въпреки че беше (много много) глупава ситуация. И така, във въпроса си казах

така че го деактивирах (csrf на spring-security.xml) и все още имам проблема.

Не, не съм го деактивирал. Опитвах се да го деактивирам

<!--
<csrf/>
-->

Но трябва да правя:

<csrf disabled="true"/>

Коментирането на csrf етикет НЕ деактивира csrf, това е така, защото csrf е активиран по подразбиране! След намирането на проблема е наистина лесно да се каже, че е глупава грешка, но тъй като добавих csrf таг, за да го активирам, реших, че коментирането му ще го деактивира. Намерете отговора в Пролетна документация

Сега да се върна към моя проблем. За да коригирате съобщението за грешка 405 в POST AJAX повикване С РАЗРЕШЕН CSRF, беше наистина лесно. Поддържам csrf параметрите в JS променливи по следния начин:

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

и тогава моето ajax повикване изглежда така:

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

    ...
});

Работя като чаровник. Надявам се това да помогне на някого в бъдеще.

person rmpt    schedule 27.12.2015
comment
Благодаря много ми помогна - person Kamaraju; 18.05.2017
comment
Благодаря. Това ми помогна. Искам вашето мнение за следното - имам няколко RestControllers, които връщат JSON отговор и малко връщащи изгледи. Ако активирам csrf, когато правя ajax извикване за почивка на контролери, ако се провали с грешка 405. какво трябва да правя Деактивиране на csrf? или изпращане на csrf токен в ajax повиквания? Кое се препоръчва? - person Kuldeep Singh; 21.11.2019
comment
трябва да изпращате вашия csrf токен при всяко повикване. само от съображения за безопасност. ако нямате нищо против проблемите със сигурността, тогава можете просто да деактивирате csrf - person rmpt; 21.11.2019

В моя случай, със същия проблем, помага това:

  • добавете taglib:
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
  • добавете в тялото на jsp:
<sec:csrfMetaTags />
  • добавете в ajax
headers: {"X-CSRF-TOKEN": $("meta[name='_csrf']").attr("content")}

P.S. Благодарение на Илия Шулгин, страхотен отговор, сега е тук.

person Андрей    schedule 01.06.2018