Spring CSRF token does not work, when the request to be sent is a multipart request

前端 未结 5 568
情歌与酒
情歌与酒 2020-11-27 22:24

I use,

  • Spring Framework 4.0.0 RELEASE (GA)
  • Spring Security 3.2.0 RELEASE (GA)
  • Struts 2.3.16

In which, I use an in-built securi

相关标签:
5条回答
  • 2020-11-27 22:55

    In case of Spring Boot + Security + CSRF + Multipart, multipart files get binding to neither ModelAttribure nor RequestParam (MultipartFile file)

    Below Code Worked fine for me.

    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

    security.filter-order=2
    
    0 讨论(0)
  • 2020-11-27 22:59

    If you are using @annotations, and the jsp view like this:

        <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 :

    @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

    The multipart/form-data filter (MultipartFilter) needs to be registered before the SecurityConfig that enables the CSRF. You can do it with this:

    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, "/*");
    
    }
    }
    
    0 讨论(0)
  • 2020-11-27 22:59

    You can disable csrf - httpSecurity.csrf().disable();

     @Configuration
        public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            ...
            httpSecurity.csrf().disable();
            ...
        }
    }
    
    0 讨论(0)
  • 2020-11-27 23:01

    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 in the applicationContext.xml file has to be registered as follows

    <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. 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

    <url-pattern>/*</url-pattern>
    

    This is a bug in the documentation.

    This will work just fine, in case, it is Spring MVC alone.

    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.

    0 讨论(0)
  • 2020-11-27 23:02

    I solved this problem by:

    • sending the multi-part file using vanilla javascript, like in Mozilla's guide
    • adding the _csrf token in the HTML header, in meta tags, like in the Spring guideline for sending the CSRF token with Ajax
    • instead of using jquery, adding it directly to the XHR object

      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);
      
    0 讨论(0)
提交回复
热议问题