Thymeleaf Webflux Security, Csrf не добавляется в представления

Я пытаюсь добавить теги csrf в формы, но похоже, что он работает иначе, чем в mvc.

Итак, я добавил <input type = "hidden" th:name = "${_csrf.parameterName}" th:value = "${_csrf.token}" />

для входа в форму, однако атрибут _csrf отсутствует, хотя эти аннотации присутствуют

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity

Вот мой SecurityWebFilterChain:

 http
                .authorizeExchange().pathMatchers(
                "/landing",
                "/",
                "/register",
                "/login",
                "/favicon.ico",
                "/js/**",
                "/fonts/**",
                "/assets/**",
                "/css/**",
                "/webjars/**").permitAll()
                .anyExchange().authenticated()
                .and()
                .httpBasic()
                .and()
                .formLogin().loginPage("/login")
                .and().logout()

Что мне не хватает?

ОБНОВИТЬ: Добавлены зависимости, которые я использую, которые связаны.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
    <thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version>

</properties>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
            <version>3.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.github.jpenren</groupId>
            <artifactId>thymeleaf-spring-data-dialect</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.9.RELEASE</version>
        </dependency>
    </dependencies>

ОБНОВИТЬ Когда я включаю скрытый тег ввода с csrf в форму входа:

<input type = "hidden" th:name = "${_csrf.parameterName}" th:value = "${_csrf.token}" />

Я получаю такую ​​ошибку:

org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "_csrf.parameterName" (template: "public/login" - line 75, col 17)

Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'parameterName' cannot be found on null

Потому что _csrf по какой-то причине имеет значение null, хотя аннотации есть.

Контроллер входа в систему:

 @GetMapping("/login")
    public String login(Model model) {
        return "public/login";
    }

Также попытался добавить такой совет контроллера:

@ControllerAdvice
public class SecurityAdvice {

    @ModelAttribute("_csrf")
    Mono<CsrfToken> csrfToken(final ServerWebExchange exchange) {

        final Mono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());

        return csrfToken.doOnSuccess(token -> exchange.getAttributes()
                .put(DEFAULT_CSRF_ATTR_NAME, token)).log();
    }
}

Точно так же, как здесь: https://github.com/daggerok/csrf-spring-webflux-mustache

Однако это приводит к

java.lang.NullPointerException: null
    at com.a.Config.SecurityAdvice.csrfToken(SecurityAdvice.java:23) ~[classes/:na]

Эта строка является возвращаемой частью последнего фрагмента.

Вы получаете какие-либо ошибки?

Supun Dharmarathne 19.06.2018 16:32

@SupunDharmarathne Обновил вопрос.

Grego 19.06.2018 16:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
2
868
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Это то, что я сделал, чтобы он заработал.

@GetMapping("/login")
public Mono<String> login(ServerWebExchange exchange, Model model) {
    Mono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
    return token.map(t -> {
        model.addAttribute("_csrf", t);
        return "login";
    });
}

В связи с этим, знаете ли вы, почему я получаю «Invalid CSRF Token», когда enctype формы является multipart, но в остальном работает нормально? Иногда мне нужно загружать файлы, и это требует использования multipart. Открыл еще вопрос: stackoverflow.com/questions/52074742/…

Grego 29.08.2018 18:47

Мне пришлось включить thymeleaf-extras-springsecurity5 (не springsecurity4), чтобы это заработало.

Я не смог найти ни одного CsrfToken, а это означало, что ответ от @ mk88 не помог, ни это руководство.

Как только я включил следующую зависимость, она работала безупречно, как и было задумано:

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        <version>3.0.4.RELEASE</version>
    </dependency>

Вывод из тимелеафа:

<form action = "/members" method = "POST" ><input type = "hidden" name = "_csrf" value = "432033fd-1076-4620-9f99-d0220b6d3071"/>

Самым чистым решением, которое будет работать на всех ваших контроллерах, является использование @ControllerAdvice, как подробно описано в в документации:

@ControllerAdvice
public class SecurityControllerAdvice {
    @ModelAttribute
    Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
        Mono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());
        return csrfToken.doOnSuccess(token -> exchange.getAttributes()
                .put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token));
    }
}

Похоже, что информация CsrfToken фактически не добавляется в модель по умолчанию в WebFlux. (см. https://github.com/spring-projects/spring-security/issues/6046)

Обходной путь - добавить ControllerAdvice, который подписывается на информацию о токене и ссылается на него через ComponentScan или объявляет его как явный bean-компонент, например:

(Я использую Kotlin, но на Java это должно работать точно так же)

import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME
import org.springframework.security.web.server.csrf.CsrfToken
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono

@ControllerAdvice
class CsrfControllerAdvice {

    @ModelAttribute(value = DEFAULT_CSRF_ATTR_NAME)
    fun csrfToken(exchange: ServerWebExchange): Mono<CsrfToken> {
        return exchange.getAttributeOrDefault(CsrfToken::class.java.name, Mono.empty())
    }
}

Другие вопросы по теме