问题描述
我用,
- Spring Framework 4.0.0 发布(正式版)
- Spring Security 3.2.0 发布(正式版)
- Struts 2.3.16
其中,我使用内置的安全令牌来防范 CSRF 攻击.
In which, I use an in-built security token to guard against CSRF attacks.
Struts 表单如下所示.
The Struts form looks like the following.
<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>
生成的HTML代码如下.
The generated HTML code is as follows.
<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>
这很好用,除非请求是多部分的,在这种情况下,请求以状态代码 403 结束.
This works fine, unless the request is multipart in which case, the request ends with the status code 403.
HTTP 状态 403 - 在请求中发现无效的 CSRF 令牌null"参数_csrf"或标头X-CSRF-TOKEN".
输入状态报告
message 在请求参数中发现无效的 CSRF 令牌 'null'_csrf"或标题X-CSRF-TOKEN".
message Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
描述已禁止访问指定资源.
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>
那么,当请求是多部分的时,在哪里寻找这个令牌?(这根本不应该与 Struts 相关.)
So, where to look for this token, when a request is multipart? (This should not be related to Struts at all.)
放置MultipartFilter
在 Spring Security 之前 也没有帮助.
Placing MultipartFilter
before Spring Security did not help either.
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>
推荐答案
在这种情况下,由于它是一个多部分请求,其中 CSRF 令牌对 Spring 安全不可用,除非 MultipartFilter
和 MultipartResolver
已正确配置,以便 Spring 可以处理多部分请求.
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.
MulipartResolver
必须如下注册
<bean id="filterMultipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="-1" />
</bean>
maxUploadSize
的属性值-1
对上传的文件大小没有限制.该值可能因要求而异.多个文件时,文件大小为所有上传文件的大小.
The attribute value -1
of maxUploadSize
puts no limit on the uploaded file size. This value may vary depending upon the requirements. In case of multiple files, the file size is the size of all uploaded files.
还有,
<servlet-name>/*</servlet-name>
的
MultipartFilter
的
需要改成
<url-pattern>/*</url-pattern>
这是 文档.
这将工作得很好,以防万一,它是单独的 Spring MVC.
This will work just fine, in case, it is Spring MVC alone.
但是如果它是Spring和Struts(2)的集成,它会在关联的Struts动作类中引发另一个问题.上传文件的信息在关联的 Struts 动作类中将为 null
.
but if it is an integration of Spring and Struts(2), it incurs another problem in the associated Struts action class. The information of the uploaded file will be null
in the associated Struts action class(es).
To solve this particular issue, see this answer to customize a multipart request.
这篇关于Spring CSRF 令牌不起作用,当要发送的请求是多部分请求时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!