I want to upload files from my angularJS client. I have enabled CSRF protection and it works fine except when i try to upload a file, where i get a 403
error:
Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'.
But the token is in the request headers and is correct! When i disable the CSRF protection i can upload the files without any problem.
The CSRF protection works fine besides that.
Here is my current configuration:
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());
}
}
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);
}
}
And last i have added a SecurityWebApplicationInitializer as pointed here .
SecurityWebApplicationInitializer.java
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
What am i doing wrong?
Update: i added this config but i still get a 403.
@Configuration
public class MultipartUploadConfig {
@Bean(name = "filterMultipartResolver")
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
}
A quick solution if you are OK with sending the CSRF Token as a url parameter. In your html template.
<form .... th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}">
...
</form>
If you use Ajax, put token parameter in url, to action in form action:
url: "/uploadFile?${_csrf.parameterName}=${_csrf.token}"
It becomes like this:
function uploadFile() {
$.ajax({
url: "/uploadFile?${_csrf.parameterName}=${_csrf.token}",
type: "POST",
data: new FormData($("#upload-file-form")[0]),
enctype: 'multipart/form-data',
processData: false,
contentType: false,
cache: false,
success: function () {
// Handle upload success
$("#upload-file-message").text("File succesfully uploaded");
},
error: function () {
// Handle upload error
$("#upload-file-message").text(
"File not uploaded (perhaps it's too much big)");
}
});
}
You can do the following to provide the token on each Ajax call:
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
There's another StackOverflow post that talks about this, that's where I found, but it's been a while since I saw it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.