Spring security — тесты сервера ресурсов oauth2

У меня возникают проблемы при тестировании сервера ресурсов oauth2 с использованием @WebMvcTest и метода POST HTTP.

Я всегда получаю код состояния 403, когда не отправляю токен csrf, даже если токен не требуется, когда я использую токен носителя.

Вот метод POST, который я хочу протестировать.

@PostMapping("/message")
public String createMessage(@RequestBody String message) {
    return String.format("Message was created. Content: %s", message);
}

Вот моя конфигурация безопасности:

http.authorizeRequests(authorizeRequests -> authorizeRequests       
   .antMatchers("/message/**")
   .hasAuthority("SCOPE_message:read")
   .anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer ->               
    oauth2ResourceServer
    .jwt(withDefaults())
);

Я следую тестам, представленным в примерах пружинная безопасность.

Предполагалось, что следующий тест будет пройден, но он не пройден, потому что токен csrf не отправляется в запросе.

mockMvc.perform(post("/message").content("Hello message")
    .with(jwt(jwt -> jwt.claim("scope", "message:read")))
    .andExpect(status().isOk())
    .andExpect(content().string(is("Message was created. Content: Hello message")));

Когда я добавляю токен csrf в запрос, тест проходит:

mockMvc.perform(post("/message").content("Hello message")
    .with(jwt(jwt -> jwt.claim("scope", "message:read")))
    .with(csrf()))
    .andExpect(status().isOk())
    .andExpect(content().string(is("Message was created. Content: Hello message")));

Когда я запускаю приложение, нет необходимости отправлять токен csrf в запросе POST.

Я разветвил репозиторий GitHub Spring Security, и проект с этим неудачным тестом доступен по адресу ссылка.

Есть ли способ настроить мои тесты, чтобы мне не нужно было отправлять токен csrf в запросе POST?


person henriquels    schedule 18.07.2019    source источник
comment
Вы сказали, что когда я запускаю приложение, нет необходимости отправлять токен csrf в запросе POST. Как вы это пробовали? Токен CSRF требуется для всех запросов POST, если он явно не отключен с помощью .csrf(csrf -> csrf.disable()) в вашей конфигурации безопасности. Все тесты примера проекта предназначены для запросов GET, поэтому им не требуется токен CSRF.   -  person Eleftheria Stein-Kousathana    schedule 19.07.2019
comment
На самом деле токен CSRF не требуется во время выполнения. Когда я запускаю следующую команду: curl -v -H "Authorization: Bearer $TOKEN" -d "my message" -X POST localhost:8080/message я получаю статус 200. Насколько я понимаю, фильтр CSRF проверяет токен только в том случае, если токена Bearer нет.   -  person henriquels    schedule 19.07.2019
comment
Моя ошибка, вы правы, что токен CSRF не нужен, если есть токен Bearer. Проблема с тестом, который включает только постпроцессор jwt, заключается в том, что, хотя он создает контекст безопасности, содержащий JWT, он не создает токен носителя в запросе (что и ищет фильтр CSRF).   -  person Eleftheria Stein-Kousathana    schedule 19.07.2019


Ответы (1)


Чтобы фильтр CSRF обнаружил, что вы используете токен JWT, вам нужно будет включить токен JWT в свой запрос в качестве заголовка Authorization или в качестве параметра запроса.
Тесты, которые вы упомянули, имеют фиктивный JwtDecoder, что означает, что вы можете использовать любую строку в качестве токена и имитировать декодированное значение.
Тогда ваш тест будет выглядеть так:

Jwt jwt = Jwt.withTokenValue("token")
        .header("alg", "none")
        .claim("scope", "message:read")
        .build();
when(jwtDecoder.decode(anyString())).thenReturn(jwt);
mockMvc.perform(post("/message")
        .content("Hello message")
        .header("Authorization", "Bearer " + jwt.getTokenValue()))
        .andExpect(status().isOk())
        .andExpect(content().string(is("Message was created. Content: Hello message")));

Если вы не издеваетесь над JwtDecoder, вам нужно будет получить действительный токен носителя и передать его в заголовке Authorization.

person Eleftheria Stein-Kousathana    schedule 19.07.2019
comment
это сработало, спасибо! Но нельзя ли использовать JwtRequestPostProcessor в запросе POST? Код для тестирования метода GET стал чище. - person henriquels; 19.07.2019
comment
@henriquels JwtRequestPostProcessor не добавляет заголовок, необходимый фильтру CSRF. Вы можете написать собственный постпроцессор, подобный этот. - person Eleftheria Stein-Kousathana; 21.07.2019