Spring Security Spring Cloud Gateway后面提供的Angular应用程序无法向后端发送POST请求,因为CSRF令牌无效

8ehkhllq  于 2023-03-18  发布在  Spring
关注(0)|答案(1)|浏览(175)

我正在试验通过SpringCloudGateway提供Angular应用程序的可能性。
对于GET请求,一切都很正常,但是当我试图通过网关向资源服务发送POST请求时,请求由于“无效的CSRF令牌”而失败。
这可能是因为一些错误的配置,但我不能找到它。而且,在线文档的Spring Web关和Angular 是不是很有用的这个特定的场景。
如果您想复制此行为,下面是具有当前配置的存储库:github repo .

当前行为:来自Angular的POST请求因“无效CSRF令牌”而失败:

error: "Invalid CSRF Token"
message: "Http failure response for http://gateway:8000/api/post: 403 Forbidden"
name: "HttpErrorResponse"
​ok: false
​status: 403
​statusText: "Forbidden"
​url: "http://gateway:8000/api/post"

预期行为:POST请求成功执行,未禁用spring security中的csrf。

这是我对网关的Spring Security配置:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) throws Exception {

        return http
                .authorizeExchange(exchange -> exchange
                        .pathMatchers("/**").permitAll()
                        .anyExchange().authenticated())
                .csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
                .build();
    }
}

网关还包含以下WebFilter:

@Component
@Configuration
public class CsrfCookieWebFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String key = CsrfToken.class.getName();
        Mono<CsrfToken> csrfToken = null != exchange.getAttribute(key) ? exchange.getAttribute(key) : Mono.empty();
        return csrfToken.doOnSuccess(token -> {
            ResponseCookie cookie = ResponseCookie.from("XSRF-TOKEN", token.getToken())
                    .maxAge(Duration.ofHours(1))
                    .httpOnly(false)
                    .path("/")
                    .sameSite(Cookie.SameSite.LAX.attributeValue())
                    .build();
            exchange.getResponse().getCookies().add("XSRF-TOKEN", cookie);
        }).then(chain.filter(exchange));
    }
}

Angular 应用程序AppModule包含HttpClientXsrfModule的导入。

frebpwbc

frebpwbc1#

简短回答:RTFM并仔细检查您导入的CsrfToken(有一个用于WebMVC,另一个与另一个用于WebFlux的包不同)。
此外,与Angular应用程序一样,再次检查X-XSRF-TOKEN标头的位置是否正确(如果请求URI是绝对的,Angular不会设置此标头:使用this.httpClient.post('/api/post', {}, {observe: 'response'})而不是this.httpClient.post('http://gateway:8000/api/post', {}, {observe: 'response'}),这也需要通过网关为Angular应用程序提供服务)

从Sping Boot 3(spring-security 6)开始,必须提供一个过滤器来将CSRF cookie添加到响应中,Cookie(Server)CsrfTokenRepository已经不够了。
这里针对servlet记录了这一点,那里针对React式应用程序记录了这一点(第二个是配置spring-cloud-gateway时要参考的)。本文档包含每种情况下要复制/粘贴的确切配置
另外,根据应用程序的性质导入正确的CsrfToken时要非常小心,否则标记将为空:request / exchange属性的名称为CsrfToken.class.getName(),您可以从org.springframework.security.web.csrforg.springframework.security.web.server.csrf导入该属性,而不会出现任何编译错误(第一个用于servlet,第二个用于webflux)。CSRF cookie不是由我的网关示例设置的,直到我意识到我已经在我的WebFilter =〉中导入了CsrfCookie的servlet版本CSRF内标识值未解析

正确验证CSRF令牌

如文档中所述,应将Xor(Server)CsrfTokenRequestAttributeHandlerhandle方法用作csrf请求处理程序(仅限handle方法,而非完整的Xor(Server)CsrfTokenRequestAttributeHandler示例)

spring-cloud-gateway的工作配置

@Bean
WebFilter csrfCookieWebFilter() {
    return (exchange, chain) -> {
        // Make sure you import
        // org.springframework.security.web.server.csrf.CsrfToken
        // and not
        // org.springframework.security.web.csrf.CsrfToken
        Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
        return csrfToken.doOnSuccess(token -> {
        }).then(chain.filter(exchange));
    };
}

SecurityWebFilterChain中:

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

相关问题