在 gin 中间件中判断请求 ip 是否合法需先配置 engine.forwardedbyclientip = true,再解析 x-real-ip 或 x-forwarded-for 获取真实 ip,然后用封装的 ipwhitelist 中间件支持 cidr 和单 ip 校验。

怎么在 Gin 中间件里判断请求 IP 是否合法
Gin 本身不直接暴露真实客户端 IP,ctx.ClientIP() 返回的可能是反向代理后的地址,直接用它做白名单会失效。必须先确认你的服务是否在 Nginx、Cloudflare 等代理后面——绝大多数生产环境都是。
实操建议:
- 如果用了反向代理,确保 Nginx 配置了
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $remote_addr; - 在 Gin 启动时调用
engine.ForwardedByClientIP = true,否则ctx.ClientIP()不会解析X-Forwarded-For - 白名单检查应放在中间件最前面,避免后续逻辑误执行
写一个可复用的 IP 白名单中间件函数
别每次都在路由里手写 if 判断,封装成中间件才符合 Gin 的设计习惯。关键点是:支持 CIDR(如 192.168.1.0/24),支持 IPv4 和 IPv6,且不依赖外部包。
示例逻辑(精简版):
func IPWhitelist(ips ...string) gin.HandlerFunc {
parsed := make([]*net.IPNet, 0, len(ips))
for _, ip := range ips {
if _, net, err := net.ParseCIDR(ip); err == nil {
parsed = append(parsed, net)
} else if ipAddr := net.ParseIP(ip); ipAddr != nil {
// 单 IP 转成 /32 或 /128
mask := net.CIDRMask(32, 32)
if ipAddr.To4() == nil {
mask = net.CIDRMask(128, 128)
}
parsed = append(parsed, &net.IPNet{IP: ipAddr, Mask: mask})
}
}
return func(c *gin.Context) {
realIP := c.ClientIP()
ip := net.ParseIP(realIP)
if ip == nil {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "invalid ip"})
return
}
allowed := false
for _, net := range parsed {
if net.Contains(ip) {
allowed = true
break
}
}
if !allowed {
c.AbortWithStatus(http.StatusForbidden)
return
}
c.Next()
}
}
使用:r.Use(IPWhitelist("127.0.0.1", "192.168.0.0/16"))
为什么 c.ClientIP() 有时返回 127.0.0.1 或 ::1
这是最常见的坑:本地开发时用 curl http://localhost:8080,或测试时没走代理,c.ClientIP() 就会退化为 c.Request.RemoteAddr 解析结果,也就是本机回环地址。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
排查步骤:
- 打印
c.Request.Header.Get("X-Forwarded-For")和c.Request.Header.Get("X-Real-IP"),看是否有值 - 检查
engine.ForwardedByClientIP是否设为true(默认是false) - 确认没有多个
X-Forwarded-For值被拼接(比如 Nginx 多次追加),c.ClientIP()默认只取第一个
调试时可在中间件开头加:log.Printf("real-ip: %s, xff: %s, remote: %s", c.ClientIP(), c.Request.Header.Get("X-Forwarded-For"), c.Request.RemoteAddr)
线上部署时要注意的兼容性细节
Cloudflare、阿里云 SLB、腾讯云 CLB 对转发头的处理不一致,不能假设所有环境都用 X-Real-IP。
安全与兼容建议:
- 优先信任
X-Forwarded-For最右端 IP(即最接近用户的那个),但前提是你的入口代理是可信的;否则攻击者可伪造该头 - 如果你的流量一定经过某一层可信代理(比如 Nginx),就只信任
X-Real-IP,并忽略其他头 - IPv6 白名单要显式写成
::1/128,不要只写::1,否则net.ParseCIDR会失败 - Gin v1.9+ 对
ClientIP()的解析逻辑有微调,老版本建议锁定 v1.9.1 以上再上线白名单逻辑
真实环境里,IP 来源链路越长,头信息越不可信;白名单不是加了就万事大吉,得清楚每一层谁在改、谁在传、谁在信。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










