Как получить доступ к токену CSRF в тесте модулей контроллера Spring?
У меня есть контроллер Spring, который после проверки подлинности GET из "/user" возвращает следующий JSON:
{"name":<name>,"token":<csrf-token>}
Я попытался создать модульный тест для контроллера, который проверит, что возвращаемый JSON содержит динамически сгенерированный токен CSRF:
@Autowired
private FilterChainProxy springSecurityFilterChain;
private MockMvc mvc;
@Before
public void setUp()
throws Exception
{
...
mvc = standaloneSetup(controller)
.apply(springSecurity(springSecurityFilterChain))
.build();
}
@Test
public void getUser()
throws Exception
{
CsrfRequestPostProcessor csrfPostProcessor = null;
mvc.perform(get("/user").with(user(Const.USER)).with(csrfPostProcessor = csrf()))
.andExpect(status().isOk())
.andExpect(content().json("{\"name\":\"" + Const.NAME + "\",\"token\":\"" + csrfPostProcessor.toString() + "\"}"));
}
Тест не проходит по следующим направлениям:
Failed tests:
ControllerTest.getUser:74 token
Expected: org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors$CsrfRequestPostProcessor@203c20cf
got: 565a95b0-d0bb-4376-a8a0-725a3b16a787
Есть ли способ исправить это, и если нет, то есть ли способ создать альтернативный тест, который будет использовать динамически генерируемый токен CSRF?
1 ответ
Вот одно из возможных решений - создать собственный сопоставитель, который перехватывает токен CSRF в формате UUID4:
private static Pattern CSRF_PATTERN = Pattern.compile("[\\da-f]{8}-[\\da-f]{4}-4[\\da-f]{3}-[\\da-f]{4}-[\\da-f]{12}");
public static Matcher<String> isCsrf(StringBuilder intercept) {
return new ArgumentMatcher<String>() {
@Override
public boolean matches(Object obj) {
// intercept may be null
Assert.isTrue(obj instanceof DefaultCsrfToken, "obj");
String token = ((DefaultCsrfToken)obj).getToken();
if (intercept != null) {
intercept.setLength(0);
intercept.append(token);
}
java.util.regex.Matcher matcher = CSRF_PATTERN.matcher(token);
return matcher.matches();
}
};
}
Затем используйте его следующим образом:
@Test
public void getUser()
throws Exception
{
StringBuilder token = new StringBuilder();
mvc.perform(get("/user").with(user(Const.USER)).with(csrf()))
.andExpect(status().isOk())
.andExpect(request().attribute("_csrf", isCsrf(csrf)))
.andExpect(content().json("{\"name\":\"" + Const.NAME + "\",\"token\":\"" + token.toString() + "\"}"));
}
В качестве бонуса вы получите валидатор токена CSRF.