You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway适配Angular的CSRF配置问题及解决方案咨询

Spring Cloud Gateway作为OAuth2客户端时的CSRF配置问题解答

问题回顾

我尝试将Spring Security官方文档中的配置适配到响应式应用(以Spring Cloud Gateway作为BFF,配置为OAuth2客户端),配置代码如下:

http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
        .csrfTokenRequestHandler(new XorServerCsrfTokenRequestAttributeHandler()::handle);

但仍出现“An expected CSRF token cannot be found”错误,且浏览器调试工具中找不到XSRF-TOKEN Cookie。

随后定义了如下WebFilter:

@Bean
WebFilter csrfCookieWebFilter() {
    return (exchange, chain) -> {
        Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
        return csrfToken.doOnSuccess(token -> {
        }).then(chain.filter(exchange));
    };
}

现在浏览器能获取到XSRF-TOKEN Cookie,却出现“Invalid CSRF Token”错误。

我的疑问:

  • CookieServerCsrfTokenRepository到底是什么?
  • 为何在Spring Cloud Gateway中使用它无法生成CSRF令牌Cookie?
  • 如何配置Spring Cloud Gateway以允许Angular应用向/logout端点发送PUT请求?

补充说明:此前1个半月有类似问题未得到解答,已在Spring Security提交工单。


疑问解答

1. CookieServerCsrfTokenRepository是什么?

CookieServerCsrfTokenRepository是Spring Security提供的CSRF令牌存储实现,核心职责包括:

  • 生成CSRF令牌并将其存储到名为XSRF-TOKEN的Cookie中(默认名称可自定义)
  • 从请求头X-XSRF-TOKEN中提取令牌,完成验证逻辑
  • 支持配置Cookie的HttpOnly、Secure等属性,withHttpOnlyFalse()方法专门用于允许前端JavaScript读取Cookie,适配Angular这类自动携带X-XSRF-TOKEN头的框架。

2. Spring Cloud Gateway中无法自动生成CSRF Cookie的原因

在响应式Web应用(如Spring Cloud Gateway)中,CSRF令牌采用懒加载机制:只有当服务器需要验证CSRF令牌,或者显式订阅了CsrfToken的Mono时,才会触发令牌生成和Cookie写入。

初始配置中没有触发令牌生成的逻辑——Spring Cloud Gateway作为BFF,若请求未进入需要CSRF验证的端点(如POST/PUT/DELETE等写操作端点),服务器不会主动生成CSRF令牌,这就是一开始浏览器中没有XSRF-TOKEN Cookie的原因。

3. 解决Invalid CSRF Token错误的配置方案

添加的WebFilter触发了令牌生成,但出现验证错误,是因为XorServerCsrfTokenRequestAttributeHandler会对令牌进行异或处理(用于BREACH攻击防护),而Angular发送的是Cookie中原始的令牌值,两者不匹配。

正确配置需兼顾BREACH防护和Angular的自动令牌携带逻辑,步骤如下:

步骤1:配置正确的CSRF处理器组合

使用XorServerCsrfTokenRequestAttributeHandler配合默认逻辑,允许读取原始令牌进行验证:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    XorServerCsrfTokenRequestAttributeHandler delegate = new XorServerCsrfTokenRequestAttributeHandler();
    // 允许从请求头中读取未异或的原始令牌
    delegate.setCsrfRequestAttributeName(null);

    http.csrf(csrf -> csrf
            .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
            .csrfTokenRequestHandler(delegate));
    
    // 补充你的OAuth2客户端等其他配置
    return http.build();
}

步骤2:保留触发令牌生成的WebFilter

你定义的WebFilter会主动订阅CsrfToken,触发令牌生成和Cookie写入,需保留:

@Bean
WebFilter csrfCookieWebFilter() {
    return (exchange, chain) -> {
        Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
        return csrfToken.doOnSuccess(token -> {
            // 订阅后会自动写入Cookie,无需额外操作
        }).then(chain.filter(exchange));
    };
}

4. 允许Angular向/logout发送PUT请求

Spring Security默认的/logout端点接受GET请求,Angular通常发送PUT/POST等写请求,可通过两种方式配置:

方案1:配置/logout接受PUT方法并验证CSRF(推荐)

让logout端点支持PUT方法,同时依赖之前的CSRF配置完成验证:

http.logout(logout -> logout
        .logoutUrl("/logout")
        .allowedMethods(HttpMethod.PUT, HttpMethod.POST, HttpMethod.GET));

此时Angular会自动从XSRF-TOKEN Cookie中提取令牌,放入X-XSRF-TOKEN请求头,配合CSRF配置即可通过验证。

方案2:忽略/logout的CSRF验证(仅适合特定场景)

若无需对logout请求做CSRF验证,可直接忽略:

http.csrf(csrf -> csrf
        .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
        .csrfTokenRequestHandler(delegate)
        .ignoringAntMatchers("/logout"));

内容的提问来源于stack exchange,提问作者ch4mp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 11:15:05