首页 文章

CSRF保护阻止我上传文件

提问于
浏览
1

我创建了一个带有Spring Boot和Spring安全性的简单应用程序,其中包含:

  • 登录表单

  • 一个"upload"表单(在后端有一个关联的控制器)

Problem :Spring安全性具有内置的默认CSRF保护 . 它适用于常见的REST调用,但它阻止我上传文件:我收到此错误消息:

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

如果我停用CSRF保护,我可以成功上传文件 .

我创建了一个SSCCE来说明问题 . 重现的步骤是:

  • 启动应用程序(主类为 com.denodev.Application

  • 连接到 localhost:8080

  • 使用这些凭据进行身份验证:

  • 登录: user

  • 密码: password

  • 重定向到"upload"表单时,尝试上传任何文件 .

  • 在类 Application 中,随意激活/停用CSRF保护,重新启动应用程序并重试 .

代码的相关部分是:

@RestController
@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class);
  }

  @RequestMapping(value = "/upload-file", method = RequestMethod.POST)
  @ResponseBody
  public String uploadFile(@RequestParam("file") MultipartFile file) {
    return "Successfully received file "+file.getOriginalFilename();
  }

  @Configuration
  @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
  protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
      http
          .authorizeRequests()
          .antMatchers("/", "/**/*.html", "login").permitAll()
          .anyRequest().authenticated()
          .and()
            .formLogin()
            .successHandler(successHandler())
            .failureHandler(failureHandler())
          .and()
            .exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler())
            .authenticationEntryPoint(authenticationEntryPoint())
          .and()

          //1 : Uncomment to activate csrf protection
          .csrf()
          .csrfTokenRepository(csrfTokenRepository())
          .and()
          .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)

          //2 : Uncomment to disable csrf protection
          //.csrf().disable()
      ;
    }

    /**
     * Return HTTP 200 on authentication success instead of redirecting to a page.
     */
    private AuthenticationSuccessHandler successHandler() {
      return new AuthenticationSuccessHandler() {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
          httpServletResponse.setStatus(HttpServletResponse.SC_OK);
        }
      };
    }

    /**
     * Return HTTP 401 on authentication failure instead of redirecting to a page.
     */
    private AuthenticationFailureHandler failureHandler() {
      return new AuthenticationFailureHandler() {
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
          httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
          httpServletResponse.getWriter().write(e.getMessage());
        }
      };
    }

    /**
     * Return HTTP 403 on "access denied" instead of redirecting to a page.
     */
    private AccessDeniedHandler accessDeniedHandler() {
      return new AccessDeniedHandler() {
        @Override
        public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
          httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
          httpServletResponse.getWriter().write(e.getMessage());
        }
      };
    }

    private AuthenticationEntryPoint authenticationEntryPoint() {
      return new AuthenticationEntryPoint() {
        @Override
        public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
          httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
          httpServletResponse.getWriter().write(e.getMessage());
        }
      };
    }

What I tried :

Spring security's documentation关于在Spring安全性之前放置 MultipartFilter 的Multipart建议 . 它通过编辑 web.xml 文件很好地解释了如何使用普通的旧webapp . 这不适用于Spring Boot,我无法确定什么是等效语法 .

我尝试用注释 @BeanOrder 公开 MultipartFilter 有几个选项,但我仍然在努力 .

有任何想法吗?

1 回答

  • 2

    这对我有用:

    添加指令以在客户端上载文件:

    app.directive('fileModel', function ($parse) {
    
            return {
    
                restrict: 'A',
    
                link: function(scope, element, attrs) {
    
                    var model = $parse(attrs.fileModel);
                    var modelSetter = model.assign;
    
                    element.bind('change', function(){
    
                        scope.$apply(function(){
                            modelSetter(scope, element[0].files[0]);
                        });
    
                    });
    
                }
        };
    })
    

    上传文件:

    <input type="file" file-model="fileToUpload"/>
    

    这是我将文件上传到服务器的方式:

    var formData = new FormData();
    
    formData.append("file", fileToUpload);
    
    $http({
    
            method: 'POST',
            url: 'the URL',
            headers: {'Content-Type': undefined},
            data: formData,
            transformRequest: angular.identity
    
    })
    
    .success(function(data, status){
    
    })
    

相关问题