Spring Boot + Security - 启用CSRF时无法上传文件(Multipart)

Chr*_*tis 5 java spring multipart spring-security

我想从我的angularJS客户端上传文件.我启用了CSRF保护,它工作正常,除非我尝试上传文件,我得到一个403错误:

在请求参数'_csrf'或标题'X-XSRF-TOKEN'上找到无效的CSRF令牌'null'.

但令牌位于请求标头中并且是正确的!当我禁用CSRF保护时,我可以毫无问题地上传文件.

除此之外,CSRF保护工作正常.

这是我目前的配置:

SecurityConfiguration.java

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityUserDetailsService securityUserDetailsService;
    @Autowired
    private AuthFailureHandler authFailureHandler;
    @Autowired
    private AjaxAuthSuccessHandler ajaxAuthSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .exceptionHandling()
                .authenticationEntryPoint(authFailureHandler)
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/",
                        "/index.html",
                        "/styles/**",
                        "/bower_components/**",
                        "/scripts/**"
                ).permitAll().anyRequest()
                .authenticated()
                .and().formLogin().loginPage("/login").permitAll()
                .and().logout().logoutUrl("/logout").logoutSuccessHandler(ajaxAuthSuccessHandler).permitAll()
                .and().httpBasic()
                .and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
                .csrf().csrfTokenRepository(csrfTokenRepository());
    }

    private CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        return repository;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(securityUserDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

}
Run Code Online (Sandbox Code Playgroud)

CsrfHeaderFilter.java

public class CsrfHeaderFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
            String token = csrf.getToken();
            if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");
                response.addCookie(cookie);
            }
        }
        filterChain.doFilter(request, response);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后我添加了一个SecurityWebApplicationInitializer,如此处所示.

SecurityWebApplicationInitializer.java

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
        insertFilters(servletContext, new MultipartFilter());
    }

}
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

更新:我添加了此配置,但我仍然得到403.

@Configuration
public class MultipartUploadConfig {

    @Bean(name = "filterMultipartResolver")
    public MultipartResolver multipartResolver() {
        return new CommonsMultipartResolver();
    }
}
Run Code Online (Sandbox Code Playgroud)

nin*_*hen 2

如果您同意将 CSRF 令牌作为 url 参数发送,这是一个快速解决方案。在你的 html 模板中。

<form .... th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}">
...
</form>
Run Code Online (Sandbox Code Playgroud)