gin中需配置settrustedproxies并确保nginx等前端透传x-real-ip或x-forwarded-for,否则c.clientip()将返回代理ip而非真实客户端ip。

如何在Gin中间件中获取真实客户端IP
直接用 c.ClientIP() 很可能拿到的是代理或负载均衡器的IP,而不是用户真实出口IP。Gin默认按 X-Forwarded-For → X-Real-IP → RemoteAddr 顺序取值,但前提是你的前端(Nginx、ALB、CDN等)必须正确透传并设置这些头。
常见错误现象:c.ClientIP() 总是返回 127.0.0.1 或内网地址;白名单校验始终失败。
- 确认反向代理已配置
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - 若服务部署在云厂商SLB后(如阿里云ALB),需开启“透传客户端源IP”功能,并确保ALB监听协议为HTTP/HTTPS(非TCP/UDP)
- Gin启动时显式信任代理网段,否则会忽略
X-Forwarded-For头:router := gin.New() router.SetTrustedProxies([]string{"10.0.0.0/8", "192.168.0.0/16"}) // 根据实际代理网段调整
用map实现轻量级IP白名单中间件
不依赖数据库或外部服务时,用 map[string]struct{} 做内存白名单最简单高效,适合固定IP或少量动态更新场景。
注意点:Go中map非并发安全,若需运行时增删IP,必须加锁或改用 sync.Map;但多数白名单是启动时加载,无需运行时修改。
- 定义白名单变量:
var ipWhitelist = map[string]struct{}{ "192.168.1.100": {}, "2001:db8::1": {}, } - 中间件写法:
func IPWhitelistMiddleware() gin.HandlerFunc { return func(c *gin.Context) { ip := c.ClientIP() if _, ok := ipWhitelist[ip]; !ok { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "IP not allowed"}) return } c.Next() } } - 注册中间件:
router.Use(IPWhitelistMiddleware())
从配置文件或环境变量加载白名单
硬编码IP不利于维护,尤其当白名单频繁变动或需多环境区分时。推荐从JSON配置或环境变量初始化。
环境变量方式更轻量,适合K8s ConfigMap或Docker部署;JSON文件适合复杂结构(比如带描述、生效时间)。
- 环境变量示例(用逗号分隔):
WHITELIST_IPS="192.168.1.1,10.0.0.5,2001:db8::2" - 解析逻辑:
ips := strings.Split(os.Getenv("WHITELIST_IPS"), ",") ipWhitelist = make(map[string]struct{}) for _, ip := range ips { ip = strings.TrimSpace(ip) if ip != "" { ipWhitelist[ip] = struct{}{} } } - 注意IPv6地址含冒号,不能被shell误分割;建议统一用JSON格式避免歧义
为什么不能只校验RemoteAddr
c.Request.RemoteAddr 是TCP连接对端地址,经过Nginx、Cloudflare等代理后,它永远是上一跳代理的IP(比如 172.18.0.3:42123),完全不可信。
典型错误写法:
// ❌ 错误:永远拿不到真实用户IP ip, _, _ := net.SplitHostPort(c.Request.RemoteAddr)
- 只要上游有任意一层代理(包括Docker网桥、Service Mesh sidecar),
RemoteAddr就失效 - 即便没代理,某些网络设备(如NAT网关)也可能做端口映射,导致IP不可靠
- 唯一能信任的来源是经可信代理透传的
X-Real-IP或X-Forwarded-For最左IP(需配合SetTrustedProxies)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











