不能直接用 gin 的 c.clientip() 提取真实 ip,因其默认仅信任 127.0.0.1,线上经 nginx/slb/cdn 代理后返回内网地址;需调用 settrustedproxies 配置云厂商回源网段并解析 x-forwarded-for。

为什么不能直接用 Gin 的 c.ClientIP() 提取真实 IP
因为 c.ClientIP() 默认只信任 127.0.0.1,线上走 Consul 注册的服务几乎都部署在 Nginx / SLB / CDN 后,它返回的会是内网代理地址(比如 10.0.0.3 或 100.64.1.5),不是用户真实出口 IP。不配置可信代理网段,黑白名单就等于没生效。
必须显式调用 engine.SetTrustedProxies([]string{"100.64.0.0/10", "192.168.0.0/16"}),否则 Gin 不会解析 X-Forwarded-For;网段要精确匹配你实际使用的云厂商回源段(阿里云 SLB 是 100.64.0.0/10,腾讯云 CLB 是 100.64.0.0/11)。
- 配置错网段或留空,
c.ClientIP()仍返回代理地址,规则永远不命中 -
X-Real-IP只适合单层代理(如 Nginx 直连 Go),多层时必须用X-Forwarded-For并取最左非私有 IP - 提取后务必用
net.ParseIP()校验合法性,别直接字符串比较——IPv6 带方括号、地址含空格都会导致匹配失败
Consul 配置项怎么定义才支持 CIDR 网段和热更新
Consul KV 中存黑白名单,不能只存纯 IP 字符串,得支持 "192.168.0.0/16" 和 "2001:db8::/32" 这类 CIDR 表达式。推荐用 JSON 结构,例如:
{
"blacklist": ["198.51.100.25", "203.0.113.0/24"],
"whitelist": ["1.2.3.4", "::1"]
}
加载时用 viper.UnmarshalKey("ip_filter", &config) 解析进结构体,再逐条调用 net.ParseCIDR() 转成 *net.IPNet 存入内存 map。注意 IPv4 和 IPv6 必须分开解析,共用一个 *net.IPNet 会 panic。
- 别用
strings.Contains()做网段匹配——它对"192.168.1.100"在"192.168.0.0/16"中返回 false - Consul watch 触发更新时,先全量解析新规则,成功后再用
atomic.StorePointer()替换旧指针,避免中间态不一致 - 配置中心若返回空值或格式错误,应 fallback 到上一版规则,而不是清空名单放行所有流量
Gin 中间件里怎么安全比对 IP 并避免阻塞
中间件函数签名必须是 func(*gin.Context),且必须调用 c.Next();否则后续 handler 完全不执行。真实 IP 提取逻辑要和 Consul 规则查询解耦:前者在中间件开头做(只读操作),后者查内存里的 atomic.Value 包裹的 map,全程无锁。
黑名单优先于白名单:先查是否在黑名单(blacklistMap.Contains(ip)),命中直接 c.AbortWithStatusJSON(403, gin.H{"error": "forbidden"});白名单模式下,不在列表中就拒绝,不要漏掉 127.0.0.1 或 ::1 这类调试地址。
- 别在中间件里调
consul.KV.Get()或viper.ReadInConfig()—— 每次请求都查 Consul,QPS 上千时直接拖垮服务 - 用
sync.RWMutex保护规则加载逻辑(仅限管理接口如/admin/reload),校验阶段完全读内存 - 路径豁免很重要:对
/healthz、/metrics这类探针路径,应提前return跳过过滤,否则健康检查会被误拦
Consul 客户端初始化最容易被忽略的三个坑
Consul 客户端默认无超时,kv.Get() 卡住几秒是常态,尤其网络抖动时。必须手动配置 http.Transport,否则服务启动或配置变更会卡死。
-
Timeout设为3 * time.Second,避免阻塞主线程;KeepAlive禁用或设短(30 * time.Second),防 Consul 重启后长连接僵死 - Watch 机制要用
client.KV().List()+wait参数轮询,别用长连接监听——Go 微服务常驻进程,连接断开后不会自动重连 - Consul 返回的 value 是
[]byte,需用json.Unmarshal()解析,别直接string()强转,否则中文或特殊字符会乱码
规则热更新不是“改完 Consul 就立刻生效”,中间件读的是本地内存副本;watch 回调里解析失败、panic 或没替换 atomic.Value,都会导致新规则静默丢失。上线前务必用 curl 模拟伪造头、跨网段请求验证兜底行为。











