tutorial - spring security example
El token de Spring CSRF no funciona, cuando la solicitud que se envĂa es una solicitud de varias partes (4)
Si está utilizando @annotations, y la vista jsp tiene este aspecto:
<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>
esto puede ayudar:
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;
}
...
SecurityConfig.java amplía WebSecurityConfigurerAdapter y es la configuración de SpringSecurity
El filtro multipart / form-data (MultipartFilter) debe registrarse antes de SecurityConfig que habilita el CSRF. Puedes hacerlo con esto:
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, "/*");
}
}
Yo suelo,
- Spring Framework 4.0.0 RELEASE (GA)
- Spring Security 3.2.0 LANZAMIENTO (GA)
- Struts 2.3.16
En el cual, utilizo un token de seguridad incorporado para protegerme contra los ataques CSRF.
La forma Struts se parece a la siguiente.
<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>
El código HTML generado es el siguiente.
<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>
Esto funciona bien, a menos que la solicitud sea multiparte en cuyo caso, la solicitud finaliza con el código de estado 403.
Estado de HTTP 403 - Token de CSRF no válido ''nulo'' se encontró en el parámetro de solicitud ''_csrf'' o en el encabezado ''X-CSRF-TOKEN''.
tipo informe de estado
mensaje Token CSRF no válido ''null'' se encontró en el parámetro de solicitud ''_csrf'' o en el encabezado ''X-CSRF-TOKEN''.
descripción El acceso al recurso especificado ha sido prohibido.
El archivo spring-security.xml
es el siguiente.
<?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>
Entonces, ¿dónde buscar este token, cuando una solicitud es multiparte? (Esto no debería estar relacionado con Struts en absoluto).
La implementación de UserDetailsService
se puede encontrar en esta pregunta anterior, si es necesario.
Colocar MultipartFilter
antes de Spring Security tampoco ayudó.
El archivo web.xml
parece a lo siguiente.
<?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>
Solo funciona, cuando el token se anexa como un parámetro de cadena de consulta de la siguiente manera que, sin embargo, se desaconseja.
<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>
Puede deshabilitar csrf - httpSecurity.csrf (). Disable ();
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
...
httpSecurity.csrf().disable();
...
}
}
Resolví este problema por:
- enviando el archivo de varias partes usando vainilla javascript, como en la guía de Mozilla
- agregando el token _csrf en el encabezado HTML, en metaetiquetas, como en la guía Spring para enviar el token CSRF con Ajax
en lugar de usar jquery, agregarlo directamente al objeto 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);
En este caso, dado que se trata de una solicitud multiparte en la que el token CSRF no está disponible para la seguridad Spring, a menos que MultipartFilter
junto con MultipartResolver
esté configurado correctamente para que la solicitud multiparte pueda ser procesada por Spring.
MulipartResolver
en el archivo applicationContext.xml
debe registrarse de la siguiente manera
<bean id="filterMultipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="-1" />
</bean>
El valor de atributo -1
de maxUploadSize
no pone límite al tamaño del archivo cargado. Este valor puede variar dependiendo de los requisitos. En el caso de archivos múltiples, el tamaño del archivo es el tamaño de todos los archivos cargados.
También,
<servlet-name>/*</servlet-name>
de <filter-mapping>
de MultipartFilter
necesita ser cambiado a
<url-pattern>/*</url-pattern>
Esto es un error en la documentación .
Esto funcionará bien, en caso de que sea Spring MVC solo.
pero si se trata de una integración de Spring y Struts (2), incurre en otro problema en la clase de acción Struts asociada. La información del archivo cargado será null
en la (s) clase (s) de acción de Struts asociada.
Para resolver este problema en particular, consulte esta respuesta para personalizar una solicitud de varias partes .