看看Spring Security官方的推荐:https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart
基本上有两种方法可以做到这一点:(1) 将 MultipartFilter 放置在 Spring Security 过滤器之前,(2) 在表单操作中包含 CSRF 令牌,就像您所做的那样。第一个选项是推荐的:
第一个选项是确保指定了 MultipartFilter
在 Spring Security 过滤器之前。指定多部分过滤器
Spring Security过滤器之前表示没有授权
用于调用 MultipartFilter 这意味着任何人都可以放置
服务器上的临时文件。但是,只有授权用户才能
能够提交由您的应用程序处理的文件。在
一般来说,这是推荐的方法,因为临时文件
上传对大多数服务器的影响可以忽略不计。
为了确保 MultipartFilter 在带有 java 配置的 Spring Security 过滤器之前指定,用户可以覆盖 beforeSpringSecurityFilterChain ,如下所示:
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
为了确保在具有 XML 配置的 Spring Security 过滤器之前指定 MultipartFilter,用户可以确保将 MultipartFilter 的元素放置在 web.xml 中的 springSecurityFilterChain 之前,如下所示:
<filter>
<filter-name>MultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
请注意,如果您仍然想使用表单操作,则查询参数可能会泄漏。尝试将“headerName”更改为“parameterName”:
<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">
EDIT:如果您无法切换到基于 servlet 的容器(例如 Jetty 或 Tomcat)并且表单操作建议不起作用,最近有一个堆栈溢出线程 https://stackoverflow.com/questions/21514074/spring-csrf-token-does-not-work-when-the-request-to-be-sent-is-a-multipart-requ/43122490#43122490讨论这个问题。
一位开发人员报告使用 AJAX 解决了该问题:
我通过以下方式解决了这个问题:
- 使用 vanilla javascript 发送多部分文件,例如Mozilla 的指南 https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript#Dealing_with_binary_data
- 在 HTML 标头中的元中添加 _csrf 标记
标签,就像 Spring 指南中的使用 Ajax 发送 CSRF 令牌 https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-include-csrf-token-ajax
- 不使用jquery,直接将其添加到XHR对象中
var csrfToken = $("meta[name='_csrf']").attr("content");
var csrfHeader = $("meta[name='_csrf_header']").attr("content");
XHR.setRequestHeader(csrfHeader, csrfToken);
XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary);
XHR.send(data);
同一开发商将此问题报告给 Spring https://github.com/spring-projects/spring-security/issues/4265,但还没有引起人们的注意。