Spring Cloud Gateway非GET请求返回403 Forbidden问题求助
背景
前端采用Angular 15,后端基于Spring 3.0.2,前后端认证统一使用Keycloak 20.0.3。项目拆分多个微服务部署在独立Spring工程中,通过Spring Cloud Gateway作为前端统一入口,借助Eureka实现网关的服务发现能力。
注:网关仅通过YAML文件配置,未集成
Spring Security或Keycloak,已配置CORS规则。
网关核心配置YAML如下:
spring: application: name: gateway cloud: gateway: globalcors: corsConfigurations: '[/**]': allowedOrigins: "https://localhost:4200" allowedMethods: "*" allowedHeaders: "*" routes: - id: users uri: lb://micro-service-1/api/users predicates: - Method=GET,POST,PUT,DELETE - Path=/api/users/** - id: assets uri: lb://micro-service-2/api/assets predicates: - Method=GET,POST,PUT,DELETE - Path=/api/assets/** server: port: 8100 eureka: client: registerWithEureka: true serviceUrl: defaultZone: http://localhost:8761/eureka
问题描述
构建CRUD API过程中,GET请求可正常通过网关访问微服务,但POST、PUT、DELETE请求在Angular端发起时会返回403 Forbidden错误。
注:使用Postman测试所有请求均无异常;跳过网关直接调用微服务时,所有请求均可正常执行。
注:后端仅提供RESTful API,登录/登出/注册等认证流程完全由Keycloak处理。
已尝试的无效方案
- 网关配置
security.enable-csrf: false禁用CSRF,无效。 - 引入
spring-boot-starter-security依赖,配置SecurityFilterChain禁用CSRF、CORS、HttpBasic并允许所有请求,结果仍要求凭证且与Keycloak冲突。 - 尝试为网关集成Keycloak,因Keycloak依赖
Spring Web,与Spring Cloud Gateway产生依赖冲突。
可行解决方案
1. 修复CORS配置,允许凭证传递
当前CORS配置未开启allowCredentials,Angular发送非GET请求时会携带Keycloak认证Cookie、CSRF Token等凭证,若网关未允许凭证跨域传递,浏览器会拦截请求返回403。
修改网关CORS配置:
spring: cloud: gateway: globalcors: corsConfigurations: '[/**]': allowedOrigins: "https://localhost:4200" allowedMethods: "*" allowedHeaders: "*" allowCredentials: true # 开启凭证跨域传递 exposedHeaders: "XSRF-TOKEN" # 允许Angular读取CSRF Token响应头
2. 修正网关路由路径配置
当前路由的uri包含/api/users路径,同时Path谓词匹配/api/users/**,会导致请求转发到微服务时路径重复(如/api/users/api/users/xxx),部分微服务的安全规则会拦截这类无效路径。
调整路由配置(二选一即可):
方案A:移除uri中的路径,通过过滤器剥离前缀
routes: - id: users uri: lb://micro-service-1 predicates: - Method=GET,POST,PUT,DELETE - Path=/api/users/** filters: - StripPrefix=2 # 剥离请求路径前2段(/api/users),转发到微服务根路径
方案B:调整Path谓词匹配规则
routes: - id: users uri: lb://micro-service-1/api/users predicates: - Method=GET,POST,PUT,DELETE - Path=/** # 匹配所有路径,直接转发到uri指定的/api/users下
3. 确保CSRF Token正确传递
Angular默认会自动通过X-XSRF-TOKEN请求头发送CSRF Token,若网关未正确转发相关头信息,微服务的CSRF防护会拦截非GET请求。
在网关添加响应头配置,确保Angular能获取到CSRF Token:
spring: cloud: gateway: default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin - AddResponseHeader=Access-Control-Expose-Headers, XSRF-TOKEN
同时确认微服务侧(若集成Spring Security)的CSRF配置,允许从请求头读取Token:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringRequestMatchers("/api/**")); // 若API无需CSRF防护,可直接忽略 // 其他认证授权配置 return http.build(); }
4. 调整Keycloak Cookie的SameSite属性
Keycloak默认设置的认证Cookie(如KEYCLOAK_SESSION)SameSite属性为Strict,会导致跨域请求(Angular到网关)无法携带该Cookie,微服务会判定请求未认证返回403。
在Keycloak管理控制台操作:进入对应Realm → 客户端 → 前端客户端 → 设置 → 登录设置,将SameSite Cookie设置为Lax(HTTP环境)或None(HTTPS环境)。
内容的提问来源于stack exchange,提问作者galalem

