У меня есть приложение Webflux, защищенное Spring Security, в котором защита CSRF включена по умолчанию. Однако я не могу получить маркер CSRF для сохранения в сеансе.
После некоторых исследований я заметил, что это может быть от WebSessionServerCsrfTokenRepository.class
. В этом классе есть метод generateToken
, который должен создать Mono из сгенерированного токена CSRF:
public Mono<CsrfToken> generateToken(ServerWebExchange exchange) {
return Mono.fromCallable(() -> {
return this.createCsrfToken();
});
}
private CsrfToken createCsrfToken() {
return new DefaultCsrfToken(this.headerName, this.parameterName, this.createNewToken());
}
private String createNewToken() {
return UUID.randomUUID().toString();
}
Однако, даже если метод generateToken
вызывается CsrfWebFilter
, метод createCsrfToken
никогда не вызывается, и я никогда не получаю токен CSRF, который будет сохранен в сеансе. Моя точка останова никогда не переходит в метод createCsrfToken
, это может означать, что он никогда не подписывается.
Я работаю на Netty
с Spring Boot 2.1.0.RELEASE
и Spring Security 5.1.1.RELEASE
.
Я воспроизвел проблему в пустом примере приложения, просто содержащем следующие зависимости:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
Я что-то упустил или есть проблема с Spring Security?
UPDATE
Из дальнейших исследований я думаю, что проблема связана с этим методом в Spring Security CsrfWebFilter.class
:
private Mono<Void> continueFilterChain(ServerWebExchange exchange, WebFilterChain chain) {
return Mono.defer(() -> {
Mono<CsrfToken> csrfToken = this.csrfToken(exchange);
exchange.getAttributes().put(CsrfToken.class.getName(), csrfToken);
return chain.filter(exchange);
});
}
Здесь, csrfToken
Mono никогда не подписывается. Когда я переписываю фильтр таким образом, мне удается добавить токен в сеансе:
private Mono<Void> continueFilterChain(ServerWebExchange exchange, WebFilterChain chain) {
return Mono.defer(() -> {
return this.csrfToken(exchange)
.map(csrfToken -> exchange.getAttributes().put(CsrfToken.class.getName(), csrfToken))
.then(chain.filter(exchange));
});
}
Однако параметр _csrf
никогда не добавляется в мою модель Thymeleaf, поэтому следующий тест не работает:
<form name="test-csrf" action="/test" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<button type="submit">Escape!</button>
</form>