Контроллеры тестирования модулей с защитой CSRF включены в весеннюю безопасность
Недавно мы внедрили CSRF-защиту для нашего проекта, который использует весеннюю безопасность 3.2.
После включения CSRF некоторые из модульных тестов терпят неудачу из-за того, что токен csrf отсутствует в запросе. Я поместил некоторое фиктивное значение в параметр _csrf, и это не сработало.
Есть ли в любом случае, что я могу получить токен csrf перед отправкой запроса (при модульном тестировании)?
Ответы
Ответ 1
Ваш ответ uiroshan побеждает назначение токена csrf: в вашей конфигурации это будет постоянное значение (если ваша конфигурация не используется только в тестовом контексте, но вы ее не указали).
Правильный (и более простой) способ решения этой проблемы:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
...
@Test
public void testLogin() throws Exception {
this.mockMvc.perform(post("/login")
.param("username", "...")
.param("password", "...")
.with(csrf()))
.andExpect(status().isFound())
.andExpect(header().string("Location", "redirect-url-on-success-login"));
}
Важная часть: .with(csrf())
, которая добавит ожидаемый параметр _csrf
в запрос.
Статический метод csrf()
предоставляется spring-security-test
:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.2.13.RELEASE / 5.1.6.RELEASE</version>
<scope>test</scope>
</dependency>
Для вашего модульного теста потребуется следующий импорт:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
Ответ 2
Я нашел работу, чтобы исправить эту проблему, создав собственную реализацию CsrfTokenRepository. Это всегда будет генерировать постоянный токен (например, test_csrf_token). Таким образом, мы можем отправить этот токен в качестве параметра запроса (поскольку он не изменится) с другими параметрами формы. Ниже приведены шаги, которые я предпринял для решения моей проблемы.
-
создать класс, реализующий интерфейс CsrfTokenRepository. Реализовать генерировать токен с некоторым постоянным значением токена.
public CsrfToken generateToken(HttpServletRequest request) {
return new DefaultCsrfToken(headerName, parameterName, "test_csrf_token");
}
@Override
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
if (token == null) {
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute(sessionAttributeName);
}
} else {
HttpSession session = request.getSession();
session.setAttribute(sessionAttributeName, token);
}
}
@Override
public CsrfToken loadToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return (CsrfToken) session.getAttribute(sessionAttributeName);
}
-
Добавьте ссылку на тег csrf в своей конфигурации безопасности.
<http>
<csrf token-repository-ref="customCsrfTokenRepository" />
....
</http>
<beans:bean id="customCsrfTokenRepository" class="com.portal.controller.security.TestCsrfTokenRepository"></beans:bean>
-
Измените свои тестовые примеры, добавив параметр запроса csrf.
request.addParameter("_csrf", "test_csrf_token");
Ответ 3
В дополнение к ответу @Thierry существует аналогичное решение для реактивного стека.
При вызове вашего бэкэнда с помощью WebTestClient
:
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf
// ...
webTestClient.mutateWith(csrf()).post()...