【发布时间】:2014-02-27 07:36:24
【问题描述】:
我用过,
- Spring Framework 4.0.0 发布(GA)
- Spring Security 3.2.0 发布(GA)
- Struts 2.3.16
其中,我使用内置的安全令牌来防范 CSRF 攻击。
<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>
这是一个多部分请求,其中 CSRF 令牌对 Spring 安全性不可用,除非正确配置 MultipartFilter 和 MultipartResolver,以便 Spring 处理多部分请求。
web.xml中的MultipartFilter配置如下。
<?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>
<url-pattern>/*</url-pattern>
</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>
而在applicationContext.xml,MultipartResolver注册如下。
<bean id="filterMultipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="-1" />
</bean>
CSRF 令牌现在由 Spring 安全性接收,但这样做会在 Struts 中引发另一个问题。
上传的文件现在在 Struts 动作类中为 null,如下所示。
@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="struts-default")
public final class CategoryAction extends ActionSupport implements Serializable, ValidationAware, ModelDriven<Category>
{
private File fileUpload;
private String fileUploadContentType;
private String fileUploadFileName;
private static final long serialVersionUID = 1L;
//Getters and setters.
//Necessary validators as required.
@Action(value = "AddCategory",
results = {
@Result(name=ActionSupport.SUCCESS, type="redirectAction", params={"namespace", "/admin_side", "actionName", "Category"}),
@Result(name = ActionSupport.INPUT, location = "Category.jsp")},
interceptorRefs={
@InterceptorRef(value="defaultStack", "validation.validateAnnotatedMethodOnly", "true"})
})
public String insert(){
//fileUpload, fileUploadContentType and fileUploadFileName are null here after the form is submitted.
return ActionSupport.SUCCESS;
}
@Action(value = "Category",
results = {
@Result(name=ActionSupport.SUCCESS, location="Category.jsp"),
@Result(name = ActionSupport.INPUT, location = "Category.jsp")},
interceptorRefs={
@InterceptorRef(value="defaultStack", params={ "validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
public String load() throws Exception{
//This method is just required to return an initial view on page load.
return ActionSupport.SUCCESS;
}
}
发生这种情况是因为在我看来,多部分请求已经被 Spring 处理和使用,因此,Struts 不能将它作为多部分请求使用,因此,Struts 操作类中的文件对象是null。
有没有办法解决这种情况?否则,我现在只有将令牌作为查询字符串参数附加到 URL 的唯一选项,这是非常不鼓励且根本不推荐的。
<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>
长话短说:如果让 Spring 处理多部分请求,如何在 Struts 操作类中获取文件?另一方面,如果 Spring 不 用于处理多部分请求,那么它会丢失安全令牌。如何克服这种情况?
【问题讨论】:
-
也许尝试将
struts2过滤器移到Spring MultipartFilter之前。 -
如果
struts2过滤器移动到MultipartFilter之前,它会抱怨身份验证引发异常An Authentication object was not found in the SecurityContext。此外,MultipartFilter必须放在springSecurityFilterChain之前,否则令牌将不可用,以防请求是多部分请求。 -
在这种情况下,尝试将
struts2过滤模式从/*更改为*.action。 -
如果过滤器模式
*.action在struts2过滤器移动到MultipartFilter之前,则完全跳过安全策略。所有资源都无需任何身份验证即可公开访问。
标签: spring struts2 spring-security csrf csrf-protection