首页 > 解决方案 > Ajax GET 请求后表单提交失败(无效的 CSRF 令牌)。怎么修?

问题描述

我有一个提交 POST 请求的“客户详细信息”表单。这是在 Spring Boot 应用程序中,并作为 Thymeleaf 模板实现。我刚刚在表单中添加了一个小 AJAX 功能——当用户单击某个按钮时,它使用jQuery.ajax()根据客户地址查找地理代码。AJAX 调用是一个 GET 请求。

如果我输入客户表单并单击“提交”而不触摸 Ajax 按钮,它会正常提交。但是如果我先使用 Ajax 按钮,然后尝试提交表单,它会被CsrfFilter一条消息“ Invalid CSRF token found for http://...”停止。

我假设 Ajax 调用以某种方式使 CSRF 令牌过期,因此需要一个新令牌,但我该如何防止呢?

有一些类似这样的问题但在这种情况下,他们正在寻找一种将 CSRF 令牌添加到 Ajax 请求的方法。此外,我正在使用 Thymeleaf 视图,因此我将 CSRF 令牌自动插入到我的表单中——我不必手动添加它。

标签: ajaxspring-bootspring-securitycsrf

解决方案


问题是我已经实现了一个自定义过滤器来删除每个请求的安全上下文。这是我使应用程序无状态的方式,正如我在这个软件工程 SE 答案中所记录的那样。这会删除(或重置?)服务器端 CSRF 令牌,因此它不会与表单中的令牌匹配,但是,自定义过滤器会在之后触发,CsrfFilter所以我之前从未遇到过表单提交问题。

在这种情况下,虽然我有在为客户端构建表单之后但在提交之前处理的 Ajax 请求,所以我的自定义过滤器的不幸副作用导致表单提交失败。

如果 URL 位于特定目录路径下,我通过修改自定义过滤器以跳过删除上下文来纠正它:

public class SecurityContextDeletingFilter extends GenericFilterBean {

    RequestMatcher exceptedPaths = new AntPathRequestMatcher("/form-ajax/**");

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpSession session = request.getSession();

        if( exceptedPaths.matches(request)) {
            // Deleting the security context deletes the CSRF token in the session. We want to make sure this
            // does NOT occur if the user is in a form and is using Ajax features like the geocode lookup,
            // because they'll need the server to recognize the token on eventual form submission.
            logger.trace("Skipping this filter for a /form-ajax/ endpoint.");
        } else {
            if( session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) != null ) {
                session.removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
            }
        }

        filterChain.doFilter(servletRequest,servletResponse);
    }
}

推荐阅读