Ajax POST导致405(方法不允许)- Spring MVC

5
我正在尝试使用POST方法向我的Spring控制器/动作发出ajax调用,并使用@ResponseBody从服务器返回一个对象。奇怪的情况是,在添加Spring安全层之后,它停止工作了,而在此之前一切都正常。我将尝试解释我的解决方案,然后向您展示代码/截图等。
1. 经过一些研究,我发现一些答案表明问题可能与csrf机制有关,因此我禁用了它,但仍然存在问题。(下面是spring-security.xml)
2. 我进行了wireshark捕获以检查请求/响应。我的ajax请求是OK的,我的控制器声明也是OK的,但我不明白为什么,405响应指示 > 允许:GET(下面是捕获)。
3. 我尝试通过浏览器访问我的控制器操作(即进行GET请求),但我收到错误HTTP状态405 - 不支持请求方法“GET”!
4. 我尝试将RequestMapping(method ...)更改为RequestMethod.GET,请求到达控制器并正常工作,但我不希望它在GET方法上工作,我想要一个POST请求。
5. 将RequestMapping(消耗,生产,标头)更改为接受所有类型的数据,但仍然是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 Request (link smudged because I've used a simplified on my question)

响应(wireshark抓取) enter image description here

编辑 jQuery ajax调用代码

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

    ...
});

我看不到你发送任何凭据?你的403是如何实现的? - holmis83
我只有在经过身份验证后才能进入应用程序的这个区域,即第一步是进行身份验证,然后会话身份验证由Spring Security管理。正如我所提到的,如果我将jquery调用和控制器动作方法更改为GET,则可以正常工作。问题是,如果一切都配置正确,为什么使用POST会得到405错误。 - rmpt
有人能解释一下为什么它必须给出405而不是其他任何状态吗?当URI不支持给定的HTTP方法时,应该显示405,对吗? - pinkpanther
3个回答

12

经过多小时的研究和测试,我终于解决了一个非常愚蠢的问题。所以,在我的问题中,我说:

所以我禁用了它(在spring-security.xml中的csrf),但仍然存在问题。

不,我没有禁用它。我试图通过以下方式禁用:

<!--
<csrf/>
-->

但我应该做的是:

<csrf disabled="true"/>

在注释csrf标记并不会禁用csrf,这是因为csrf默认是启用的!当发现问题后很容易说这是一个愚蠢的错误,但是当我添加csrf标记来启用它时,我认为注释掉它就会禁用它。 在Spring文档中找到答案。

现在,回到我的问题。要解决启用了CSRF的POST AJAX调用中的405错误消息,非常简单。我将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',

    ...
});

运行得很好。希望能对未来的某个人有所帮助。


谢谢,它帮了我很多。 - Kamaraju
谢谢。这对我很有帮助。我想听听您的意见 - 我有一些RestControllers,它们返回JSON响应,还有一些返回视图。如果我启用csrf,在我对rest控制器进行ajax调用时,会出现405错误。我应该做什么?禁用csrf?还是在ajax调用中发送csrf令牌?哪个更推荐? - Kuldeep Singh
为了安全起见,您应该在每次调用中发送您的 CSRF 令牌。如果您不关心安全问题,那么您可以禁用 CSRF。 - rmpt

0

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


0
在我的情况下,有同样的问题,这个方法很有帮助:
  • 添加标签库:
 <%@ 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. 感谢 Illya Shulgin,很酷的回答,现在它在这里。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接