Invalid CSRF Token in POST request

2019-03-01 05:39发布

问题:

Overview
I am going to use API Gateway as the authentication which based on Spring security. I've just been following the steps in the https://spring.io/guides/tutorials/spring-security-and-angular-js/ link to create a project based on "pairs-double" module of its corresponding github project of https://github.com/spring-guides/tut-spring-security-and-angular-js.git.

Problem
The issue is the fact that when any POST request is submitted to the server the "Invalid CSRF Token" exception is thrown. An example of the thrown exception is as follows:

{
  "timestamp": 1461714933215,
  "status": 403,
  "error": "Forbidden",
  "message": "Invalid CSRF Token '1cdc44ad-43cb-44e6-b903-bec24fe903fd' was found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'.",
  "path": "/ui/test"
}

I checked an rechecked the issue but to no avail. I tested this scenario with postman and set the 'X-XSRF-TOKEN' as the header of the POST request but nothing happened.


So, as I am beginner in using Spring security approaches, I would appreciate it if anyone could suggest me a solution.

回答1:

Looking at the security configuration of that project, you will notice that a XSRF-TOKEN cookie is being added in each request using a filter. So what you have to do is take the value of that cookie and store it in X-XSRF-TOKEN header. I've made a test project with similar security configuration to test out this case, the complete code looks like this:

@RestController
@SpringBootApplication
public class TestApplication extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/**")  // Disable authentication for all requests.
            .permitAll()
            .and()
            .csrf().csrfTokenRepository(csrfTokenRepository())
            .and()
            .addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class); // Register csrf filter.
    }

    private Filter csrfHeaderFilter() {
        return new 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())) {

                        // Token is being added to the XSRF-TOKEN cookie.
                        cookie = new Cookie("XSRF-TOKEN", token);
                        cookie.setPath("/");
                        response.addCookie(cookie);
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }

    private CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        return repository;
    }

    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public String testGet() {
        return "hello";
    }

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public String testPost() {
        return "works!";
    }
}

To test this out with postman do the following:

  • Enable interceptor to start capturing cookies.
  • Perform a GET /test request and open the cookies tab. There you should notice a cookie with a name XSRF-TOKEN.
  • Take the value of that cookie and put it in X-XSRF-TOKEN header and perform a POST /test request.