Spring(Spring Boot)如何向前端提供 CSRF 令牌?
Posted
技术标签:
【中文标题】Spring(Spring Boot)如何向前端提供 CSRF 令牌?【英文标题】:How does Spring (Spring Boot) provide CSRF token to frontend? 【发布时间】:2021-03-15 17:51:02 【问题描述】:我正在尝试将 CSRF 保护添加到基于 Spring-Boot(尤其是 WebFlux)的项目中。
到目前为止我所尝试的低于安全配置。
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
http.
...
.and()
.csrf()
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
.requireCsrfProtectionMatcher(
new NegatedServerWebExchangeMatcher(
new OrServerWebExchangeMatcher(
Arrays.asList(
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, FORM_LOGIN_URL),
ServerWebExchangeMatchers
.pathMatchers(HttpMethod.GET, PERMISSION_ENDPOINT)
)
)
)
)
.accessDeniedHandler(new CustomHttpStatusServerAccessDeniedHandler(HttpStatus.FORBIDDEN))
...;
return http.build();
但是当我尝试上述配置时,我看不到任何与从服务器推送到浏览器的 CSRF 令牌相关的内容。不是默认推送给客户端吗?如果是这样,向客户端(浏览器)提供 CSRF 令牌的更简洁的方法是什么?在哪个春季文档中提到了这一点。 (我关注了这个->https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html)
附: (希望以下这些信息对答案有用)
Spring Boot Webflux 应用程序没有提供我的登录表单。它由在不同域(不同 IP 和端口)上运行的 Angular 应用程序提供服务。这就是为什么我在上面的配置中有一个FORM_LOGIN_URL
常量,用作.loginPage(FORM_LOGIN_URL)
。我已经自定义了 .authenticationSuccessHandler()
以重定向到 PERMISSION_ENDPOINT
,它一直按预期工作。
【问题讨论】:
【参考方案1】:这部分的文档记录有点差,但显然除非您订阅结果,否则 csrf-token 不会被保存。
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
final CookieServerCsrfTokenRepository cookieServerCsrfTokenRepository = new CookieServerCsrfTokenRepository();
cookieServerCsrfTokenRepository.setCookieHttpOnly(false);
http.csrf()
.csrfTokenRepository(cookieServerCsrfTokenRepository)
.and()
.cors()
...
// Add a tiny webfilter that just subscribes
@Bean
public WebFilter addCsrfToken()
return (exchange, next) -> exchange.<Mono<CsrfToken>>getAttribute(CsrfToken.class.getName())
.doOnSuccess(token -> ) // do nothing, just subscribe :/
.then(next.filter(exchange));
如果您使用功能端点,上述解决方案效果很好。您也可以使用@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));
在 github 上有一个开放的错误报告
CookieServerCsrfTokenRepository does not add cookie
【讨论】:
谢谢老兄!在过去的几天里,这对我来说是一个巨大的头痛。无论如何,让我再问你一个问题。你是如何在 github 上找到这个开放的错误报告的?当你偶然发现这样的问题时,你有没有什么技巧,如果你分享它很高兴;)。再次感谢! naah,它只是 google man.. 我通常会非常彻底地阅读 spring 文档,我会查看源代码中的内容,然后我 google 并阅读。一旦您开始了解事物如何协同工作,spring 文档就会变得如此有趣和好。不久前我建立了一个登录系统,遇到了和你一样的问题,我用谷歌搜索了CookieServerCsrfTokenRepository github
,这是第二次点击。
:D 好吧,下次我会记住您提到的内容。再次感谢伙计!以上是关于Spring(Spring Boot)如何向前端提供 CSRF 令牌?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 graphql-spring-boot 向 GraphQL Java 添加检测?