跨域带凭证时不能用"*"白名单,必须显式列出完整协议+域名;生产环境需nginx前置校验+gin动态中间件双级控制,避免浏览器静默丢弃响应。

gin 中白名单不能靠 AllowOrigins: ["*"] 解决跨域
带凭证(AllowCredentials: true)时,["*"] 会被浏览器直接拒绝,响应头根本不会生效。这不是 Gin 的 bug,而是 CORS 规范强制要求——浏览器看到 Access-Control-Allow-Origin: * 和 Access-Control-Allow-Credentials: true 同时存在,会静默丢弃整个响应。
常见错误现象:前端带 Cookie 发请求,后端明明写了 c.Header("Access-Control-Allow-Origin", "*"),但 Network 面板里看不到这个头,控制台报错 Response to preflight request doesn't pass access control check。
- 生产环境必须用完整协议+域名列表,例如
[]string{"https://admin.example.com", "https://app.example.com"} - 不要写
"https://*.example.com"——gin-contrib/cors不支持通配符匹配,只做字符串完全相等或前缀比对 - 如果前端 Origin 是
http://localhost:8080,白名单里就得显式加上它,否则本地调试也过不了
动态白名单需绕过 gin-contrib/cors 自己写中间件
gin-contrib/cors 的 Config 是初始化时固定的,不支持运行时更新或按请求动态判断。如果你的白名单来自配置中心(如 Nacos)、数据库或文件,必须手写中间件。
关键点在于:Origin 头必须校验后再设响应头,且 OPTIONS 预检必须返回 204 并携带正确头,否则浏览器拒绝后续请求。
- 先取
c.Request.Header.Get("Origin"),为空则跳过(同源请求不用设 CORS 头) - 查白名单 map(建议用
map[string]struct{},O(1) 查找),命中才写Access-Control-Allow-Origin等头 - 必须在
c.Next()之后写头,否则可能被后续 handler 覆盖;但 OPTIONS 请求要提前c.AbortWithStatus(204)返回 - 别在中间件里做网络 I/O(比如查 DB 或 HTTP 请求),否则高并发下延迟飙升
白名单热更新要用 atomic.Value 替换指针
硬编码白名单改一次就要重启服务;用 []string 遍历匹配,QPS 上千时 CPU 占用明显升高。真正可行的是用 atomic.Value 包裹 map,读写分离。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
更新时生成新 map,调用 atomic.StorePointer() 替换旧指针;读时 atomic.LoadPointer() 转成 map,全程无锁。
- 白名单数据源建议走文件监听(
fsnotify)或配置中心长轮询,避免轮询拉垮性能 - IPv6 地址要先转规范格式:
net.ParseIP(ipStr).To16()再存,否则::1和0:0:0:0:0:0:0:1会被当成两个 key - 域名标准化:统一转小写、去掉端口(如
https://a.com:8080→https://a.com),否则https://A.COM查不到
Nginx 层白名单比 Gin 层更早拦截恶意请求
把白名单逻辑全堆在 Gin 里,意味着所有请求都得进 Go runtime 才能判断是否放行。攻击者发大量非法 Origin 请求,Go 进程会白白消耗 CPU 和 goroutine 资源。
Nginx 在 TCP 层就可完成 Origin 校验,失败直接 403,连 Go 进程都不碰。用 map + if + add_header 组合,性能碾压应用层。
- 白名单定义必须放在
http块里,用map $http_origin $allow_cors { ... },不能塞进server或location -
proxy_hide_header必须关掉后端返回的 CORS 头,否则和 Nginx 自己加的冲突 - OPTIONS 预检响应必须由 Nginx 直接返回(
return 204),别转发给 Go,否则多一层 RTT 且易出错
真正复杂的不是怎么写白名单,而是谁来决定“这个 Origin 是否可信”——是前端 URL?是请求头里的 Referer?还是 JWT 里的 issuer 字段?这些语义判断没法交给 Nginx,得留在 Gin 里做。所以白名单常是 Nginx + Gin 两级:Nginx 拦明显非法,Gin 做业务级校验。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










