一次 403 错误的排查实录:Spring Cloud Gateway、WebFlux、PingFederate 与 CORS

一次 403 错误的排查实录:Spring Cloud Gateway、WebFlux、PingFederate 与 CORS



一次真实的线上排错记录:一个看似 OAuth / JWT / Spring Security 的 403,最终却是 CORS 问题。


背景

我们在一次系统集成过程中,遇到了一个非常令人困惑的问题,涉及以下组件:

  • Spring Cloud Gateway
  • Spring Boot WebFlux 后端服务
  • PingFederate(OIDC / OAuth2)
  • 本地运行的 React 前端(http://localhost:4200

整体请求流程如下:

  1. 用户通过 PingFederate 登录(SSO)
  2. 前端获取 access token
  3. 前端发起 POST 请求 → Gateway → 后端服务

在以下场景中,一切都运行正常:

  • 使用 Postman 直接调用后端
  • 绕过 Gateway,直接调用后端
  • 所有组件都在本地运行

但一旦请求通过 部署在 OpenShift 中的 Spring Cloud Gateway,所有 POST 请求都会失败,并返回:

403 Forbidden

为什么这个问题如此迷惑

  • 完全相同的 access token,在 Postman 中可以正常使用
  • GET 请求成功,POST 请求失败
  • 系统中没有配置任何 role 或 scope 校验
  • 不同环境下 401 / 403 的表现不一致
  • JWT 的 issuer、audience、签名全部正确

所有线索一开始都指向 OAuth 或 PingFederate 的配置问题——但这个判断是错误的。


关键线索:问题是如何被定位出来的

1. Postman 成功,浏览器失败

同一个请求,在不同客户端表现完全不同:

  • Postman → 成功
  • 浏览器(经 Gateway)→ 403

这一步非常关键,它强烈暗示:问题并不在 token 本身


2. 在 Postman 中加上 Origin 头,请求立刻失败

我们在 Postman 中手动加入以下 header:

Origin: http://localhost:4200

请求立刻返回:

403 Forbidden

这一实验直接证实:问题与 CORS 处理 有关。


真正的根因

Spring Security 对 CORS 的默认拦截

后端是一个 Spring Boot WebFlux 应用,并配置为 OAuth2 Resource Server:

oauth2ResourceServer(oauth2 -> oauth2.jwt())

当我们启用 issuer-uri(PingFederate)之后,Spring Security 会启用更严格、也是更正确的安全校验逻辑。当请求中包含 Origin header 时:

  • 请求会被视为 CORS 请求
  • 如果没有显式配置 CORS 策略,Spring Security 会直接拒绝
  • 最终返回 403 Forbidden

这正好解释了所有现象:

  • Postman 成功(默认不带 Origin header)
  • 浏览器 + Gateway 失败(Origin 被转发)
  • GET 通常成功(没有 preflight)
  • POST 失败(触发 CORS 校验)

为什么使用 public-key-location 时没有暴露问题

在旧的配置中,我们使用的是:

jwt:
  public-key-location: classpath:public.key

这种方式下,Spring Security 的行为相对宽松,缺失的 CORS 配置并没有立刻暴露出来。

当切换为:

jwt:
  issuer-uri: https://fed.test.example.com

系统进入了标准 OAuth2 Resource Server 模式,CORS 校验也随之生效


最终的临时解决方案(仅用于本地开发)

在本地调试阶段,我们选择在 Node Gateway 的代理层移除 Origin header:

if (process.env.NODE_ENV === "development" && headers.origin) {
  // 本地调试时避免 Spring Security 的 CORS 拦截
  delete headers.origin;
}

这样可以在不削弱生产环境安全性的前提下,继续本地开发和排错。


推荐的生产环境修复方案

正确、长期的解决方案是:在 WebFlux 后端显式配置 CORS 策略,并且只允许可信来源。

CORS 不应该被绕过,而应该被清晰、明确地管理。


总结与经验教训

  • 403 并不总是权限或角色问题
  • Postman 成功、浏览器失败时,第一时间怀疑 CORS
  • issuer-uri 启用的是更正确、更严格的安全模型
  • Gateway 会转发 Origin header,后端必须明确如何处理

这次问题再次提醒我们:很多“安全问题”,并不出现在单个系统内部, 而是隐藏在系统与系统之间的边界上。

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular posts from this blog

fixed: embedded-redis: Unable to run on macOS Sonoma

Copying MDC Context Map in Web Clients: A Comprehensive Guide

Reset user password for your own Ghost blog