一次 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)
整体请求流程如下:
- 用户通过 PingFederate 登录(SSO)
- 前端获取 access token
- 前端发起 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
Post a Comment