2015-09-30 2 views
1

Я создал простое приложение с Spring ботинком и Spring безопасностью, которая содержит:защита CSRF мешает мне загрузить файл

  • Формы Войти
  • «загрузить» форму (с соответствующим контроллером на стороне BACKEND в)

Задача: Весна безопасности имеет встроенную защиту CSRF по умолчанию. Он хорошо работает с общим REST звонками, но мне мешает загрузку файла: Я получаю сообщение об ошибке:

Недопустимого CSRF лексема «нулевой» была найдено в параметре запроса «_csrf» или заголовок «X-XSRF- ЗНАК».

Если я деактивирую защиту CSRF, я могу успешно загрузить файл.

Для иллюстрации проблемы я создал SSCCE. Шаги по воспроизведению являются:

  1. Запуск приложения (Основной класс com.denodev.Application)
  2. Подключение к localhost:8080
  3. Authenticate с этими учетными данными:
    • Логин: user
    • Пароль: password
  4. При перенаправлении в форму «загрузить» попробуйте загрузить любой файл.
  5. В классе 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()); 
     } 
     }; 
    } 

Что я пробовал:

Spring security's documentation о MULTIPART советы разместить MultipartFilter до весны безопасности. Он хорошо объясняет, как это сделать с простым старым webapp, отредактировав файл web.xml. Это неприменимо к Spring Boot, и я не могу понять, что такое эквивалентный синтаксис.

Я попытался разоблачить MultipartFilter с аннотациями @Bean и Order с несколькими вариантами, но я все еще борюсь с ним.

Любые идеи?

+0

Do вы отправляете загруженный файл в HTTP-запрос AngularJs? –

+0

@BillBilal В контексте этого SSCCE нет, но я пробовал то же самое с вызовом Angular/AJAX и получал ту же проблему. –

+0

В моем случае это работает. Я добавил директиву для загрузки файла на стороне клиента, после чего отправлю его в запрос по электронной почте AngularJs. AngularJs добавляет токен X-XSRF-TOKEN к каждому HTTP-запросу. –

ответ

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){ 

}) 
+0

Спасибо. Я тестировал и, похоже, работает благодаря «Content-Type»: undefined'. Разве это не плохая практика? –

Смежные вопросы