Spring Cloud Gateway调用微服务时遭遇403 Forbidden及CSRF令牌缺失问题
嗨,我来帮你搞定这个403的问题!你遇到的An expected CSRF token cannot be found错误,本质原因是Spring Cloud Gateway默认会自动引入Spring Security依赖并启用CSRF防护机制——哪怕你自己没手动配置任何Spring Security功能,网关的自动配置也会帮你开启这个校验。而你的MailService没加Spring Security,所以直接调用8001端口没问题,但通过网关8000转发时就触发了CSRF校验,导致403。
下面给你两种解决方案,按需选择:
方案一:直接禁用网关的CSRF防护(简单快捷,适合不需要CSRF防护的场景)
如果你的应用是内部服务调用、开发环境,或者前端和网关已经是同源且不需要额外CSRF防护,可以直接在网关的配置文件里关闭CSRF校验:
YAML格式(application.yml)
spring: security: csrf: enabled: false
Properties格式(application.properties)
spring.security.csrf.enabled=false
重启网关后,再用Angular调用应该就能正常转发了。
方案二:配置CSRF令牌传递(适合需要保留CSRF防护的生产环境)
如果需要保留CSRF防护,得让网关和Angular的CSRF令牌机制兼容——Angular默认会从Cookie中读取XSRF-TOKEN,然后在POST请求时带上X-XSRF-TOKEN请求头,所以我们需要让网关正确处理这个流程:
- 添加网关的Spring Security配置类
创建一个配置类,配置CSRF令牌的存储方式为Cookie,并且允许前端读取这个Cookie:
package com.smdev.gatewayservice.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository; @Configuration @EnableWebFluxSecurity public class GatewaySecurityConfig { @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .csrf(csrf -> csrf // 配置CSRF令牌存储到Cookie,并且允许前端读取(HttpOnly=false) .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()) ); return http.build(); } }
- 确保网关路由保留必要的请求头和Cookie
在你的RouteLocator配置中,确保不会过滤掉CSRF相关的请求头和Cookie,可以添加preserveHostHeader()或者显式保留头的配置:
@Bean public RouteLocator getRouteLocator(RouteLocatorBuilder routeLocatorBuilder){ return routeLocatorBuilder.routes() .route(p -> p .path("/api/mail/**") .filters(f -> f .dedupeResponseHeader("Access-Control-Allow-Origin", "RETAIN_UNIQUE") .preserveHostHeader() // 保留主机头,确保请求信息完整 ) .uri("http://localhost:8001")) .build(); }
这样配置后,Angular会自动从网关的Cookie中获取XSRF-TOKEN,并在POST请求时带上X-XSRF-TOKEN头,网关的CSRF校验就能通过,正常转发请求到MailService了。
补充说明
为什么直接调用8001端口没问题?因为你的MailService没有引入Spring Security依赖,所以不存在CSRF校验逻辑;而Spring Cloud Gateway的starter默认包含了Spring Security的相关依赖,自动开启了CSRF防护,这才是问题的核心。
内容的提问来源于stack exchange,提问作者Razor

