Thymeleaf Webflux 安全性,Csrf 未添加到视图中
Thymeleaf Webflux Security, Csrf not added to views
我正在尝试向表单添加 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 为空,即使注释已到位。
登录控制器:
@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]
这一行是最后一个片段的 return 部分。
这就是我为让它工作所做的。
@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";
});
}
我必须包含 thymeleaf-extras-springsecurity5
(而不是 springsecurity4
)才能正常工作。
我找不到任何 CsrfToken,这意味着 @mk88 的回答没有帮助,也没有帮助 this guide。
一旦我包含了以下依赖项,它就会按设计完美地工作:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
Thymeleaf 的输出:
<form action="/members" method="POST" ><input type="hidden" name="_csrf" value="432033fd-1076-4620-9f99-d0220b6d3071"/>
适用于所有控制器的最干净的解决方案是使用 @ControllerAdvice
详述 in the documentation:
@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));
}
}
在 WebFlux 中,默认情况下,CsrfToken 信息似乎并未实际添加到模型中。 (参见 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())
}
}
我正在尝试向表单添加 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 为空,即使注释已到位。
登录控制器:
@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]
这一行是最后一个片段的 return 部分。
这就是我为让它工作所做的。
@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";
});
}
我必须包含 thymeleaf-extras-springsecurity5
(而不是 springsecurity4
)才能正常工作。
我找不到任何 CsrfToken,这意味着 @mk88 的回答没有帮助,也没有帮助 this guide。
一旦我包含了以下依赖项,它就会按设计完美地工作:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
Thymeleaf 的输出:
<form action="/members" method="POST" ><input type="hidden" name="_csrf" value="432033fd-1076-4620-9f99-d0220b6d3071"/>
适用于所有控制器的最干净的解决方案是使用 @ControllerAdvice
详述 in the documentation:
@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));
}
}
在 WebFlux 中,默认情况下,CsrfToken 信息似乎并未实际添加到模型中。 (参见 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())
}
}