我使用Spring Boot和Spring安全性创建了一个简单的应用程序,其中包含:
问题:Spring安全性具有内置的默认CSRF保护。它适用于常见的REST调用,但它阻止我上传文件:我收到此错误消息:
在请求参数'_csrf'或标题'X-XSRF-TOKEN'上找到无效的CSRF令牌'null'。
如果我停用CSRF保护,我可以成功上传文件。
我创建了一个SSCCE来说明问题。重现的步骤是:
com.denodev.Application
)localhost:8080
user
password
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安全性之前放置MultipartFilter
的Multipart建议的Spring security's documentation。它通过编辑web.xml
文件很好地解释了如何使用普通的旧webapp。这不适用于Spring Boot,我无法确定等效语法。
我尝试使用注释MultipartFilter
和@Bean
公开Order
几个选项,但我仍然在努力解决这个问题。
有什么想法吗?
答案 0 :(得分: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){
})