正则仅粗筛格式,net.parseip 才是唯一可信解析入口;需先 strings.trimspace 再解析,cidr 用 net.parsecidr + ipnet.contains 判断,ipv6 注意压缩写法与大小写,禁止手写位运算。

为什么 ^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$ 不能直接用在 Go 的 net.ParseIP 场景里
因为这个正则只校验字符串格式,不区分 IPv4/IPv6,也不处理前导零、空格、CIDR 后缀等真实白名单字段中常见的干扰项。Go 的 net.ParseIP 对前导零敏感("010.0.0.1" 会被解析为 nil),而正则可能放过它。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先用正则做粗筛(比如排除明显非法字符、多余点号),再交给
net.ParseIP做最终判定 - IPv4 白名单条目若带 CIDR(如
"192.168.1.0/24"),必须用net.ParseCIDR,不能只靠正则 - IPv6 要特别注意压缩写法(
::1)和大小写,正则很难全覆盖,net.ParseIP才是唯一可信入口
用 net.ParseIP + strings.TrimSpace 防止白名单配置里的隐形坑
YAML/TOML/环境变量注入的 IP 字符串常带首尾空格或换行,net.ParseIP(" 127.0.0.1\n") 直接返回 nil,但人眼几乎看不出来。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 所有输入必须先
strings.TrimSpace,再传给net.ParseIP - 别信配置文件里“看起来干净”的值,尤其从 k8s ConfigMap 或 dotenv 加载时
- 如果白名单来自 HTTP 查询参数(如
?whitelist=10.0.0.1),还要额外检查 URL 解码后是否含控制字符
判断一个 IP 是否匹配 CIDR 白名单时,别手动拆 / 和位运算
有人会把 "10.0.0.0/8" 拆成 IP 和掩码长度,再手写按位与逻辑——这在 IPv4 下勉强可行,但一碰到 IPv6("2001:db8::/32")就彻底失效,net.IPNet.Contains 才是标准解法。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
_, ipnet, err := net.ParseCIDR(cidrStr)解析白名单条目 - 用
ipnet.Contains(parsedIP)判断归属,parsedIP必须是net.ParseIP返回的有效值 - 注意:同一个 CIDR 字符串可能被多次解析,建议预编译成
[]*net.IPNet缓存复用
Go 中验证白名单列表时,net.ParseIP 返回 nil 就代表非法,别捕获 panic
net.ParseIP 是纯函数,不 panic,只返回 nil。有人误以为要 recover,结果写了一堆无用的 defer+recover,还掩盖了真实错误来源。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 逐条遍历白名单切片,对每个
str执行ip := net.ParseIP(strings.TrimSpace(str)) - 如果
ip == nil,记录原始字符串和行号,用于快速定位配置错误 - IPv6 地址必须用方括号包裹才能出现在 URL 中(如
[::1]),但白名单本身不应含括号——解析前先去掉
真正麻烦的是混合场景:同一份白名单既要支持单 IP,又要支持 CIDR,还要兼容 IPv4 和 IPv6。这时候别拼正则,老实用 net.ParseIP 和 net.ParseCIDR 分两路走,错在哪条路径上一眼就能看清。










