我创建了一个带有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,我无法确定什么是等效语法 .
我尝试用注释 @Bean
和 Order
公开 MultipartFilter
有几个选项,但我仍然在努力 .
有任何想法吗?
1 回答
这对我有用:
添加指令以在客户端上载文件:
上传文件:
这是我将文件上传到服务器的方式: