简体   繁体   中英

Add CSRF Token to RestTemplate Get Request for Page Secured by Form Login

The following test fails because it returns a login page as opposed to the content.

TestRestTemplate restTemplate = new TestRestTemplate();

HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", testUser.getUser().getUid());
form.set("password", testUser.getUserPassword());

ResponseEntity<String> loginEntity = restTemplate.exchange(
  url("/login"),
  HttpMethod.POST, 
  new HttpEntity<>(form, headers), 
  String.class);

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Arrays.asList(MediaType.TEXT_HTML));
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 
// auth csrf and form login - doesn't work
ResponseEntity<String> getLoginEntity = restTemplate.getForEntity(url("/login"), String.class);
String cookie = getLoginEntity.getHeaders().getFirst(HttpHeaders.SET_COOKIE);
// contains CSRF-TOKEN
requestHeaders.set(HttpHeaders.COOKIE, cookie);
Pattern pattern = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*");
Matcher matcher = pattern.matcher(getLoginEntity.getBody());
matcher.matches();
requestHeaders.set("X-CSRF-TOKEN", matcher.group(1));
//

ResponseEntity<String> memberEntity = restTemplate.exchange(
  url("/members"),
  HttpMethod.GET,
  new HttpEntity<>(form, requestHeaders),
  String.class
);
assertEquals("Unexpected member content", "Welcome member!", memberEntity.getBody());

The configuration is like so

protected void configure(HttpSecurity http) throws Exception {
  http
    .csrf().and()
    .authorizeRequests()
      .antMatchers("/**").fullyAuthenticated()
      .and()
    .formLogin()
      .permitAll();
}

The login page returned includes _csrf as a hidden field and the value corresponds to the value which has been set in the cookie.

Note that with minor modifications, I am able to get the following scenarios to work:

However, I've had no success with

  • form login and csrf

Any guidance on how to get this to work would be appreciated.

BTW: The approach I've taken follows: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-samples/spring-boot-sample-web-secure/src/test/java/sample/web/secure/SampleSecureApplicationTests.java

It does not work because you need cookie and csrf to POST to login, so you need to make two requests at least.

First you need to setup a /csrf page that exposes the csrf token, eg: {"token": "b3c7338e-95c0-4088-9fb7-d72a870bd60c", "headerName": "X-CSRF-TOKEN", "parameterName": "_csrf" }

Then the flow is like this:

  1. Read the token from /csrf storing also the cookie passed with header Set-Cookie
  2. Prepare for POST to login: Set the token in the request header with name specified by /csrf , eg: X-CSRF-TOKEN: b3c7338e-95c0-4088-9fb7-d72a870bd60c
  3. Set the stored cookie in request header Cookie (this because cookie and csrf token are bound together on the server)
  4. POST to /login storing the cookie passed to you with response header Set-Cookie (it changes after login)
  5. For all successive requests use the cookie got from /login setting it in request header Cookie
  6. For all successive POST requests you need to get once the new token from /csrf (since cookie changed also token changed)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM