应预解析白名单CIDR为*net.IPNet切片并用Contains校验,需对字符串先trim、处理IPv6方括号、fallback补掩码,再缓存复用;真实IP须经可信代理链解析并过滤私有地址,禁用字符串匹配。

如何用 net.ParseCIDR 预解析白名单 CIDR 列表
动态白名单不是每次请求都重新解析字符串,而是启动时就把 192.168.0.0/16 这类字符串转成 *net.IPNet 对象缓存起来。否则每次调用 net.ParseCIDR 会重复做语法校验、掩码计算和不可见字符清理,QPS 上千时明显拖慢。
常见错误是把配置文件读出来的 CIDR 字符串直接丢进循环里解析——尤其当配置含换行、BOM 或空格时,net.ParseCIDR 立刻返回 nil,且不报错,导致整条规则静默失效。
- 必须对每个 CIDR 字符串先调用
strings.TrimSpace,再检查是否含/,不含就跳过或补默认掩码(如/32或/128) - IPv6 CIDR 必须带方括号,例如
[2001:db8::]/32;写成2001:db8::/32会解析失败 - 解析失败的 CIDR 应记录日志并跳过,不能 panic 或中断服务——线上配置可能临时出错
- 结果存为
[]*net.IPNet切片,而非 map,因为 CIDR 匹配本质是“属于任一网段”,无需 key 查找
怎么安全提取真实客户端 IP 并校验
别信 r.RemoteAddr,它带端口(如 10.0.1.5:42193),且在有 Nginx/Cloudflare 时永远是代理内网地址。真实 IP 要从 X-Forwarded-For 或 X-Real-IP 头取,但前提是确认上游代理可信。
典型误判:直接取 X-Forwarded-For 第一个值,结果被客户端伪造;或没过滤私有地址(如 127.0.0.1、192.168.x.x),导致内网 IP 绕过白名单。
立即学习“go语言免费学习笔记(深入)”;
- 先检查
r.Header.Get("X-Real-IP"),若为空再 fallback 到X-Forwarded-For的最左非私有 IP - 用
net.ParseIP解析后,立刻调用ip.IsGlobalUnicast()过滤掉回环、链路本地、文档地址等无效 IP - 若所有头都无效或解析失败,才退回到
r.RemoteAddr,但需先用strings.Split(r.RemoteAddr, ":")[0]剥离端口 - 最终得到的
net.IP必须非 nil,否则ipNet.Contains(ip)恒返回false
为什么不能用 strings.HasPrefix 或正则匹配 CIDR
strings.HasPrefix("192.168.1.100", "192.168.1") 看似能匹配 /24,但遇到 192.168.1.0/25 就漏判;更糟的是完全无法处理 IPv6(如 2001:db8::1/64),也搞不定前导零、大小写、压缩格式等变体。
标准库 net.IPNet.Contains 内部已处理协议族对齐(比如 IPv4-mapped IPv6 地址)、掩码位运算、字节长度差异(IPv4 是 4 字节,IPv6 是 16 字节),手动实现极易出错。
- 别写位运算:自己算网络地址、掩码与、广播地址……既冗余又易错
- 别用正则:写一个能同时覆盖
10.0.0.0/8和[2001:db8::]/32的正则几乎不可能维护 - 别依赖字符串相等:
net.IP.String()输出格式不固定(IPv6 可能带或不带压缩),==比对不可靠 - 真正该做的只有一件事:
ipNet.Contains(clientIP),其余全是干扰
高频请求下如何避免性能瓶颈
白名单中间件跑在每条 HTTP 请求路径上,如果白名单有 50 条 CIDR,每次请求都遍历调用 50 次 ipNet.Contains,虽单次 O(1),但叠加 GC 和内存访问开销,QPS 过万时延迟会上升。
更隐蔽的问题是:有人把白名单存在 map[string]struct{} 里,只支持精确 IP,不支持 CIDR——这根本不是白名单,是 IP 黑名单的反向滥用。
- 预解析后的
[]*net.IPNet是最佳结构,长度通常 ≤ 100,线性扫描成本可控 - 若白名单极大(如数万条 CIDR),应改用第三方库如
github.com/malfunkt/iprange构建查找树 - 禁止在 handler 内调用
net.ParseCIDR或net.ParseIP—— 这些函数内部有内存分配,高频下触发 GC - 注意
net.IP是切片,传递时别意外修改底层字节;必要时用ip.To4()或ip.To16()显式复制
net.ParseCIDR 静默失败。不加 strings.TrimSpace 和 error 日志,问题会藏很久。


















