openApiGateway 跨域 403 问题

openApiGateway 跨域 403 问题

记录时间:2026-08-21
环境:测试环境 / openApiGateway(Spring Cloud Gateway)
相关组件:WAF → Nginx → gzeport-openApi-gateway → 后端服务

一、问题现象

测试环境一个接口访问异常,浏览器发的 OPTIONS 预检请求:

OPTIONS http://192.168.111.x/openapiGateway/newCasLogin/casBackend/login

直接返回 403,响应体是:

{"code":403,"msg":"跨域请求不被允许"}

这明显是跨域被拦了。我先在脑子里把这条链路过了一遍:

浏览器 → WAF → Nginx → openApiGateway → 后端服务

403 到底卡在哪一层,光看现象是看不出来的,只能一层一层往下排。

二、排查过程

2.1 先翻 Nginx 配置

我的第一反应是 Nginx 层。翻了对应的 location:

location /openapiGateway {
    rewrite ^/openapiGateway(.*)$ $1 break;
    proxy_pass http://192.168.120.x:31803;

    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;
}

这里 rewrite 把前缀去掉了,请求 /openapiGateway/newCasLogin/casBackend/login 会转成 192.168.120.x:31803/newCasLogin/casBackend/login 再转发。配置里没有对这个接口单独的 denyreturn 403,Nginx 这边看不出问题。

2.2 走 Nginx 用 curl 验证

接着我用 curl 模拟浏览器的 OPTIONS 预检:

curl -i -X OPTIONS \
  'http://192.168.111.x/openapiGateway/newCasLogin/casBackend/login' \
  -H 'Origin: http://113.xxx.xxx.190:21080' \
  -H 'Access-Control-Request-Method: POST'

返回:

HTTP/1.1 403
Server: nginx/1.10.3
Content-Type: application/json;charset=UTF-8
Vary: Origin

响应体还是 {"code":403,"msg":"跨域请求不被允许"}

这里我提醒自己一句:返回头里有 Server: nginx,不能就咬定是 Nginx 拦的。Nginx 只是反向代理,完全可能把上游 Gateway 返回的 403 原样转发回来,这个头说明不了问题。

2.3 绕过 Nginx 直连 Gateway

为了搞清楚到底是不是 Nginx/WAF 的问题,我干脆绕过 Nginx,直接打 Gateway 的地址 192.168.120.x:31803

curl -i -X OPTIONS \
  'http://192.168.120.x:31803/newCasLogin/casBackend/login' \
  -H 'Origin: http://113.xxx.xxx.190:21080' \
  -H 'Access-Control-Request-Method: POST'

结果还是 403,响应体一模一样。

到这一步我心里有数了:绕过 Nginx 还是 403,那 WAF、Nginx、Nginx 的 location 配置这三层都可以排除,403 基本就是 openApiGateway 应用自己产生的。

2.4 翻技术部新增代码

既然是 Gateway 自己拦的,我就去翻技术部最近的提交 bc26b19b,发现他们新加了两个过滤器:

gzeport-openApi-gateway/
└── src/main/java/com/gzeport/openApi/gateway/filter/
    ├── CorsWebFilter.java
    └── IpFilter.java

也就是说 Gateway 最近多了两类访问控制:Origin/CORS 白名单,还有 IP 白名单。

2.5 盯住 CorsWebFilter 的匹配逻辑

CorsWebFilter.java,白名单的来源有两块,一块是代码里写死的默认值,一块是 Nacos 配置:

private static final String[] DEFAULT_ALLOWED_ORIGINS = {
        "http://test.example.cn",
        "https://example.cn"
};

@Value("${cors.allowed-origins:}")
private String allowedOriginsConfig;

@PostConstruct 里把默认值和 Nacos 配置合并成 allowedOrigins 这个 Set,启动时会打一行日志 Gateway CORS白名单已加载

请求进来后的判断是:

String origin = request.getHeaders().getFirst("Origin");

if (origin != null && !allowedOrigins.contains(origin)) {
    response.setStatusCode(HttpStatus.FORBIDDEN);
    response.getHeaders().set("Content-Type", "application/json;charset=UTF-8");
    response.getHeaders().set("Vary", "Origin");

    String body = "{\"code\":403,\"msg\":\"跨域请求不被允许\"}";
    return response.writeWith(...);
}

我盯住的就是 allowedOrigins.contains(origin) 这一行。它是 Set 的精确字符串匹配,不是前缀匹配,也不会忽略端口。

2.6 对启动日志,找到真凶

既然代码是精确匹配,那问题大概率出在白名单的值和浏览器实际 Origin 对不上。翻 Gateway 启动日志:

Gateway CORS白名单已加载:
[http://113.xxx.xxx.190,
 http://test.example.cn,
 https://example.cn]

而浏览器实际发的 Origin 是:

Origin: http://113.xxx.xxx.190:21080

对上了:白名单配的是 http://113.xxx.xxx.190,实际请求带的是 http://113.xxx.xxx.190:21080,多了个 :21080 端口。精确匹配下这两个字符串不相等,于是进了 FORBIDDEN 分支。

三、根因分析

根因一句话:技术部配置的 Gateway CORS 白名单 Origin 和前端实际 Origin 不一致,白名单漏了端口

链路是:

openApiGateway
  ↓
CorsWebFilter
  ↓
Origin 精确匹配失败
  ↓
HTTP 403

所以这次 403 和 WAF、Nginx、网络、防火墙都无关,就是 Gateway 应用代码自己返回的。

四、解决方案

代码里 @Value("${cors.allowed-origins:}") 说明白名单是可以通过 Nacos 配的。把实际 Origin 补进配置就行:

cors:
  allowed-origins: "http://113.xxx.xxx.190:21080"

如果需要同时放行带端口和不带端口的,用逗号分隔:

cors:
  allowed-origins: "http://113.xxx.xxx.190,http://113.xxx.xxx.190:21080"

Properties 格式则写:

cors.allowed-origins=http://113.xxx.xxx.190:21080

这里有个要注意的点:白名单是在 @PostConstruct 里一次性构建的,代码里没有看到对 allowedOrigins 的动态刷新实现。所以改了 Nacos 之后不能想当然认为 Gateway 会立即生效,需要先确认项目的 Nacos 动态刷新机制,必要时重启 Gateway 再看启动日志。

五、验证

本次排查的定位验证靠两步:绕过 Nginx 直连 Gateway 仍 403(排除了基础设施层),加上白名单日志和实际 Origin 的对比(定位到端口不一致)。

修复后复测,用原来的 curl 命令,重点看是否还返回 403 跨域请求不被允许

# 走 Nginx
curl -i -X OPTIONS \
  'http://192.168.111.x/openapiGateway/newCasLogin/casBackend/login' \
  -H 'Origin: http://113.xxx.xxx.190:21080' \
  -H 'Access-Control-Request-Method: POST'

# 绕过 Nginx 直连 Gateway
curl -i -X OPTIONS \
  'http://192.168.120.x:31803/newCasLogin/casBackend/login' \
  -H 'Origin: http://113.xxx.xxx.190:21080' \
  -H 'Access-Control-Request-Method: POST'

同时看 Gateway 启动日志,确认白名单已经包含带端口的 Origin:

grep "Gateway CORS白名单已加载" gateway.log

待确认:本次只定位到根因,改 Nacos 白名单后的复测和是否需重启 Gateway 尚未执行确认。

六、注意事项

6.1 Gateway 现在是两层访问控制

技术部这次同时加了两个过滤器,访问链路变成:

WAF(放行)
  ↓
Nginx(放行)
  ↓
Gateway CorsWebFilter(Origin 白名单)
  ↓
Gateway IpFilter(IP 白名单)
  ↓
后端服务

所以后面遇到 Gateway 403,要先分清是哪一层拦的。

6.2 按响应体快速定位

返回 {"code":403,"msg":"跨域请求不被允许"} 时,查 CorsWebFilter,对比白名单 Origin 和浏览器实际 Origin,注意端口:

grep "Gateway CORS白名单已加载" gateway.log

返回 {"code":403,"msg":"请求IP不在白名单中!"} 时,查 IpFilter,重点看这几个配置项:

参数 默认值 说明
globalFilter.ipList IP 列表
globalFilter.allowFlag false 是否启用 IP 白名单
globalFilter.allowIpList 允许的 IP 列表,逗号分隔

IP 白名单相关日志:

grep -E "clientIp:|不在白名单" gateway.log

6.3 责任边界

这次已经用绕过 Nginx 直连 Gateway 验证过,直连 192.168.120.x:31803 照样 403,所以不是基础设施层的问题。技术部新增 Origin/IP 白名单后,Gateway 自己具备了主动返回 403 的能力,只要 Origin 或客户端 IP 没配进白名单,就算 WAF、Nginx 都放行了,请求照样被应用层拒绝。后续发布时技术部需要同步明确:允许的 Origin、允许的客户端 IP、对应 Nacos 配置、是否需要重启、白名单是否支持动态刷新。

6.4 建议的排查顺序

以后遇到 Gateway 403,不建议一上来就查 WAF,按这个顺序来:

① 看 HTTP 响应 Body,区分 CorsWebFilter 还是 IpFilter
② 绕过 Nginx/WAF 直连 Gateway(192.168.120.x:31803)
③ 看 Gateway 日志:clientIp / Origin / 白名单
④ 最后再查 WAF / Nginx / 网络 / 防火墙

七、参考资料

  • 本次排查主要靠翻 Gateway 源码(CorsWebFilter.javaIpFilter.java)和启动日志定位,未单独查阅外部文章。
  • 背景可参考 Spring Cloud Gateway 官方 CORS 配置文档:CORS Configuration
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇