[英]Spring CSRF token does not work, when the request to be sent is a multipart request
I use,我用,
In which, I use an in-built security token to guard against CSRF attacks.其中,我使用内置的安全令牌来防范 CSRF 攻击。
The Struts form looks like the following. Struts 表单如下所示。
<s:form namespace="/admin_side"
action="Category"
enctype="multipart/form-data"
method="POST"
validate="true"
id="dataForm"
name="dataForm">
<s:hidden name="%{#attr._csrf.parameterName}"
value="%{#attr._csrf.token}"/>
</s:form>
The generated HTML code is as follows.生成的 HTML 代码如下。
<form id="dataForm"
name="dataForm"
action="/TestStruts/admin_side/Category.action"
method="POST"
enctype="multipart/form-data">
<input type="hidden"
name="_csrf"
value="3748c228-85c6-4c3f-accf-b17d1efba1c5"
id="dataForm__csrf">
</form>
This works fine, unless the request is multipart in which case, the request ends with the status code 403.这工作正常,除非请求是多部分的,在这种情况下,请求以状态代码 403 结束。
HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
HTTP 状态 403 - 在请求参数“_csrf”或标头“X-CSRF-TOKEN”上发现无效的 CSRF 令牌“null”。
type Status report
类型状态报告
message Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
在请求参数“_csrf”或标头“X-CSRF-TOKEN”上发现消息无效的 CSRF 令牌“null”。
description Access to the specified resource has been forbidden.
description已禁止访问指定的资源。
The spring-security.xml
file is as follows. spring-security.xml
文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<http pattern="/Login.jsp*" security="none"></http>
<http auto-config='true' use-expressions="true" disable-url-rewriting="true" authentication-manager-ref="authenticationManager">
<session-management session-fixation-protection="newSession">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
<csrf/>
<headers>
<xss-protection />
<frame-options />
<!--<cache-control />-->
<!--<hsts />-->
<content-type-options /> <!--content sniffing-->
</headers>
<intercept-url pattern="/admin_side/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/>
<form-login login-page="/admin_login/Login.action" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"/>
<logout logout-success-url="/admin_login/Login.action" invalidate-session="true" delete-cookies="JSESSIONID"/>
</http>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
<beans:property name="passwordEncoder" ref="encoder" />
</beans:bean>
<beans:bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref bean="daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
</authentication-manager>
<beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/>
<beans:bean id="authenticationFailureHandler" class="loginsuccesshandler.AuthenticationFailureHandler" />
<global-method-security secured-annotations="enabled" proxy-target-class="false" authentication-manager-ref="authenticationManager">
<protect-pointcut expression="execution(* admin.dao.*.*(..))" access="ROLE_ADMIN"/>
</global-method-security>
</beans:beans>
So, where to look for this token, when a request is multipart?那么,当请求是多部分的时,在哪里寻找这个令牌? (This should not be related to Struts at all.)
(这根本不应该与 Struts 相关。)
The implementation of UserDetailsService
can be found in this earlier question of mine, if needed.实施
UserDetailsService
可以发现这个矿的早期问题,如果需要的话。
Placing MultipartFilter
before Spring Security did not help either. 在 Spring Security 之前放置
MultipartFilter
也没有帮助。
The web.xml
file looks like the following. web.xml
文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<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>
<servlet-name>/*</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>AdminLoginNocacheFilter</filter-name>
<filter-class>filter.AdminLoginNocacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AdminLoginNocacheFilter</filter-name>
<url-pattern>/admin_login/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>NoCacheFilter</filter-name>
<filter-class>filter.NoCacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/admin_side/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<description>Description</description>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>struts.devMode</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
It only works, when the token is appended as a query-string parameter as follows which is however, discouraged.它仅在令牌作为查询字符串参数附加如下时才有效,但不鼓励这样做。
<s:form namespace="/admin_side"
action="Category?%{#attr._csrf.parameterName}=%{#attr._csrf.token}"
enctype="multipart/form-data"
method="POST"
validate="true"
id="dataForm"
name="dataForm">
...
<s:form>
If you are using @annotations, and the jsp view like this:如果您使用@annotations,并且jsp 视图是这样的:
<form:form id="profileForm" action="profile?id=${param.id}" method="POST"
modelAttribute="appUser" enctype="multipart/form-data" >
...
<input type="file" name="file">
...
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form:form>
this may help:这可能有帮助:
AppConfig.java : AppConfig.java :
@EnableWebMvc
@Configuration
@Import({ SecurityConfig.class })
public class AppConfig {
@Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver filterMultipartResolver() {
CommonsMultipartResolver filterMultipartResolver = new CommonsMultipartResolver();
filterMultipartResolver.setDefaultEncoding("utf-8");
// resolver.setMaxUploadSize(512000);
return filterMultipartResolver;
}
...
The SecurityConfig.java extends WebSecurityConfigurerAdapter and is the configuration for SpringSecurity SecurityConfig.java 扩展了 WebSecurityConfigurerAdapter 并且是 SpringSecurity 的配置
The multipart/form-data filter (MultipartFilter) needs to be registered before the SecurityConfig that enables the CSRF. multipart/form-data过滤器(MultipartFilter)需要在启用CSRF的SecurityConfig之前注册。 You can do it with this:
你可以这样做:
SecurityInitializer.java: SecurityInitializer.java:
public class SecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
super.beforeSpringSecurityFilterChain(servletContext);
// CSRF for multipart form data filter:
FilterRegistration.Dynamic springMultipartFilter;
springMultipartFilter = servletContext.addFilter(
"springMultipartFilter", new MultipartFilter());
springMultipartFilter.addMappingForUrlPatterns(null, false, "/*");
}
}
In this case, since it is a multipart request in which the CSRF token is unavailable to Spring security unless MultipartFilter
along with MultipartResolver
is properly configured so that the multipart request can be processed by Spring.在这种情况下,由于它是一个多部分请求,其中 CSRF 令牌不可用于 Spring 安全性,除非正确配置
MultipartFilter
和MultipartResolver
以便 Spring 可以处理多部分请求。
MulipartResolver
in the applicationContext.xml
file has to be registered as follows applicationContext.xml
文件中的MulipartResolver
必须注册如下
<bean id="filterMultipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="-1" />
</bean>
The attribute value -1
of maxUploadSize
puts no limit on the uploaded file size. maxUploadSize
的属性值-1
对上传的文件大小没有限制。 This value may vary depending upon the requirements.该值可能因要求而异。 In case of multiple files, the file size is the size of all uploaded files.
如果有多个文件,文件大小为所有上传文件的大小。
Also,还,
<servlet-name>/*</servlet-name>
of <filter-mapping>
of MultipartFilter
needs to be changed to MultipartFilter
的<filter-mapping>
需要改为
<url-pattern>/*</url-pattern>
This is a bug in the documentation .这是文档中的错误。
This will work just fine, in case, it is Spring MVC alone.这将工作得很好,以防万一它是单独的 Spring MVC。
but if it is an integration of Spring and Struts(2), it incurs another problem in the associated Struts action class.但如果它是 Spring 和 Struts(2) 的集成,则会在关联的 Struts 操作类中引发另一个问题。 The information of the uploaded file will be
null
in the associated Struts action class(es).上传文件的信息在关联的 Struts 操作类中将为
null
。
To solve this particular issue, see this answer to customize a multipart request .要解决此特定问题,请参阅此答案以自定义多部分请求。
I solved this problem by:我通过以下方式解决了这个问题:
instead of using jquery, adding it directly to the XHR object而不是使用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);
In case of Spring Boot + Security + CSRF + Multipart, multipart files get binding to neither ModelAttribure nor RequestParam (MultipartFile file)在 Spring Boot + Security + CSRF + Multipart 的情况下,multipart 文件不会绑定到 ModelAttribure 和 RequestParam(MultipartFile 文件)
Below Code Worked fine for me.下面的代码对我来说很好用。
1.MvcConfiguration.java 1.MvcConfiguration.java
@Configuration
@EnableWebMvc
@ComponentScan
public class MvcConfiguration extends WebMvcConfigurerAdapter {
.......
......
/*
* Case : Spring Boot + Security + CSRF + Mulitpart
* In this case, since it is a multipart request in which the CSRF token is unavailable to Spring security unless MultipartFilter along with MultipartResolver
* is properly configured so that the multipart request can be processed by Spring.
*
* And
*
* The multipart/form-data filter (MultipartFilter) needs to be registered before the SecurityConfig that enables the CSRF.
* So that's why
* 1. reg.setOrder(1); //below
* 2. security.filter-order=2 // in application.properties
*/
@Bean
public FilterRegistrationBean registerMultipartFilter() {
FilterRegistrationBean reg = new FilterRegistrationBean(new MultipartFilter());
reg.setOrder(1);
return reg;
}
@Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver filterMultipartResolver() {
CommonsMultipartResolver filterMultipartResolver = new CommonsMultipartResolver();
filterMultipartResolver.setDefaultEncoding("utf-8");
// resolver.setMaxUploadSize(512000);
return filterMultipartResolver;
}
.....
.....
}
2. application.properties 2. application.properties
security.filter-order=2
You can disable csrf - httpSecurity.csrf().disable();您可以禁用 csrf - httpSecurity.csrf().disable();
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
...
httpSecurity.csrf().disable();
...
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.