go-web-utils
IP 工具包 (iputil)

IsValidIP

验证 IP 地址格式是否正确

IsValidIP

验证 IP 地址字符串格式是否正确,支持 IPv4 和 IPv6 地址。

函数签名

func IsValidIP(ip string) bool

参数

  • ip string - 要验证的 IP 地址字符串

返回值

  • bool - true 表示有效的 IP 地址,false 表示无效

支持的 IP 格式

IPv4 地址

  • 标准格式:192.168.1.1
  • 零填充:192.168.001.001(会被正确识别)
  • 回环地址:127.0.0.1

IPv6 地址

  • 完整格式:2001:0db8:85a3:0000:0000:8a2e:0370:7334
  • 压缩格式:2001:db8:85a3::8a2e:370:7334
  • 回环地址:::1
  • 映射地址:::ffff:192.168.1.1

基本用法

package main

import (
    "fmt"
    "github.com/woodchen-ink/go-web-utils/iputil"
)

func main() {
    testIPs := []string{
        "192.168.1.1",      // ✅ 有效的 IPv4
        "2001:db8::1",      // ✅ 有效的 IPv6
        "::1",              // ✅ IPv6 回环地址
        "invalid-ip",       // ❌ 无效格式
        "192.168.1.256",    // ❌ 超出范围
        "",                 // ❌ 空字符串
        "192.168.1",        // ❌ 不完整的 IPv4
    }
    
    for _, ip := range testIPs {
        if iputil.IsValidIP(ip) {
            fmt.Printf("✅ %s 是有效的 IP 地址\n", ip)
        } else {
            fmt.Printf("❌ %s 不是有效的 IP 地址\n", ip)
        }
    }
}

输出:

✅ 192.168.1.1 是有效的 IP 地址
✅ 2001:db8::1 是有效的 IP 地址
✅ ::1 是有效的 IP 地址
❌ invalid-ip 不是有效的 IP 地址
❌ 192.168.1.256 不是有效的 IP 地址
❌  不是有效的 IP 地址
❌ 192.168.1 不是有效的 IP 地址

实际应用场景

1. 输入验证

func validateIPInput(w http.ResponseWriter, r *http.Request) {
    ipStr := r.FormValue("ip")
    
    if !iputil.IsValidIP(ipStr) {
        http.Error(w, "无效的 IP 地址格式", http.StatusBadRequest)
        return
    }
    
    fmt.Fprintf(w, "有效的 IP 地址: %s", ipStr)
}

2. 配置文件验证

type ServerConfig struct {
    AllowedIPs []string `json:"allowed_ips"`
    BlockedIPs []string `json:"blocked_ips"`
}

func validateConfig(config *ServerConfig) error {
    // 验证允许的 IP 列表
    for _, ip := range config.AllowedIPs {
        if !iputil.IsValidIP(ip) {
            return fmt.Errorf("无效的允许 IP: %s", ip)
        }
    }
    
    // 验证阻止的 IP 列表
    for _, ip := range config.BlockedIPs {
        if !iputil.IsValidIP(ip) {
            return fmt.Errorf("无效的阻止 IP: %s", ip)
        }
    }
    
    return nil
}

3. IP 白名单/黑名单管理

type IPFilter struct {
    whitelist map[string]bool
    blacklist map[string]bool
}

func NewIPFilter() *IPFilter {
    return &IPFilter{
        whitelist: make(map[string]bool),
        blacklist: make(map[string]bool),
    }
}

func (f *IPFilter) AddToWhitelist(ip string) error {
    if !iputil.IsValidIP(ip) {
        return fmt.Errorf("无效的 IP 地址: %s", ip)
    }
    f.whitelist[ip] = true
    return nil
}

func (f *IPFilter) AddToBlacklist(ip string) error {
    if !iputil.IsValidIP(ip) {
        return fmt.Errorf("无效的 IP 地址: %s", ip)
    }
    f.blacklist[ip] = true
    return nil
}

func (f *IPFilter) IsAllowed(ip string) bool {
    // 先检查黑名单
    if f.blacklist[ip] {
        return false
    }
    
    // 如果白名单为空,默认允许
    if len(f.whitelist) == 0 {
        return true
    }
    
    // 检查白名单
    return f.whitelist[ip]
}

4. 结合 GetClientIP 使用

func secureHandler(w http.ResponseWriter, r *http.Request) {
    // 获取客户端 IP
    clientIP := iputil.GetClientIP(r)
    
    // 验证 IP 格式
    if !iputil.IsValidIP(clientIP) {
        log.Printf("获取到无效的客户端 IP: %s", clientIP)
        http.Error(w, "无法识别客户端 IP", http.StatusBadRequest)
        return
    }
    
    // 进一步处理
    if iputil.IsPrivateIP(clientIP) {
        fmt.Fprintf(w, "内网用户,IP: %s", clientIP)
    } else {
        fmt.Fprintf(w, "外网用户,IP: %s", clientIP)
    }
}

5. API 参数验证

type IPRangeRequest struct {
    StartIP string `json:"start_ip"`
    EndIP   string `json:"end_ip"`
}

func validateIPRange(w http.ResponseWriter, r *http.Request) {
    var req IPRangeRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "无效的 JSON", http.StatusBadRequest)
        return
    }
    
    // 验证起始 IP
    if !iputil.IsValidIP(req.StartIP) {
        http.Error(w, "无效的起始 IP 地址", http.StatusBadRequest)
        return
    }
    
    // 验证结束 IP
    if !iputil.IsValidIP(req.EndIP) {
        http.Error(w, "无效的结束 IP 地址", http.StatusBadRequest)
        return
    }
    
    fmt.Fprintf(w, "IP 范围有效: %s - %s", req.StartIP, req.EndIP)
}

错误处理示例

func processIPList(ips []string) ([]string, error) {
    var validIPs []string
    var invalidIPs []string
    
    for _, ip := range ips {
        if iputil.IsValidIP(ip) {
            validIPs = append(validIPs, ip)
        } else {
            invalidIPs = append(invalidIPs, ip)
        }
    }
    
    if len(invalidIPs) > 0 {
        return validIPs, fmt.Errorf("发现 %d 个无效的 IP 地址: %v", 
            len(invalidIPs), invalidIPs)
    }
    
    return validIPs, nil
}

实现原理

IsValidIP 基于 Go 标准库的 net.ParseIP 函数实现:

func IsValidIP(ip string) bool {
    return net.ParseIP(ip) != nil
}

net.ParseIP 的特点:

  • 零内存分配:验证失败时不分配内存
  • 高性能:基于优化的解析算法
  • 标准兼容:严格遵循 RFC 标准

性能说明

  • 时间复杂度: O(n),其中 n 是 IP 字符串长度
  • 内存开销: 验证失败时零分配,成功时分配解析后的 IP 对象
  • 适用场景: 高频验证场景,如输入验证、配置检查等

相关函数

  • GetClientIP - 获取客户端真实 IP 地址
  • IsPrivateIP - 判断是否为私有网络 IP(在包文档中)