Golang中如何获取客户端的真实IP地址?

Golang中如何获取客户端的真实IP地址? 我有些犹豫不决,我想识别客户端的请求,有什么比使用IP地址更好的方法呢?但查看我的选项后,我不知道哪个更可靠:

使用 net 包的原生选项:

func PrintIP(l net.Listener) {
	fmt.Println(fmt.Sprintf("<%s> %s", l.Addr().Network(), l.Addr().String()))
}

使用 github.com/tomasen/realip 包的外部选项:

func PrintIP(r *http.Request) {
	fmt.Println(realip.FromRequest(r))
}

net.Listener 会保留客户端的真实IP吗?因为如果是这样,我倾向于使用那个选项;但如果不是,那么我想最好使用 realip.FromRequest,或者有更好的建议吗?


更多关于Golang中如何获取客户端的真实IP地址?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

net.Listener 适用于获取任意连接的 IP 地址,而 realip.FromRequest 适用于 HTTP 请求,但它很可能会返回真实客户端的 IP,而不是其使用的代理的 IP。不过,理论上这个 IP 也可能来自私有网络,而且并非所有代理都会设置能告知原始 IP 的头部信息。

最终,这取决于您的具体使用场景。

更多关于Golang中如何获取客户端的真实IP地址?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中获取客户端真实IP地址需要考虑代理和负载均衡的情况。net.Listener只能获取到直接连接的客户端IP,而无法处理经过代理的X-Forwarded-For等头部信息。

以下是更完整的解决方案:

package main

import (
    "fmt"
    "net"
    "net/http"
    "strings"
)

// 获取客户端真实IP
func GetClientIP(r *http.Request) string {
    // 检查X-Forwarded-For头部(代理服务器传递)
    xForwardedFor := r.Header.Get("X-Forwarded-For")
    if xForwardedFor != "" {
        // 可能有多个IP,取第一个
        ips := strings.Split(xForwardedFor, ",")
        return strings.TrimSpace(ips[0])
    }

    // 检查X-Real-IP头部(Nginx等代理)
    xRealIP := r.Header.Get("X-Real-IP")
    if xRealIP != "" {
        return xRealIP
    }

    // 检查CF-Connecting-IP(Cloudflare代理)
    cfConnectingIP := r.Header.Get("CF-Connecting-IP")
    if cfConnectingIP != "" {
        return cfConnectingIP
    }

    // 最后使用RemoteAddr
    ip, _, err := net.SplitHostPort(r.RemoteAddr)
    if err != nil {
        return r.RemoteAddr
    }
    return ip
}

// 处理HTTP请求的示例
func handler(w http.ResponseWriter, r *http.Request) {
    clientIP := GetClientIP(r)
    fmt.Fprintf(w, "客户端真实IP: %s\n", clientIP)
    
    // 如果需要获取监听器信息
    if server := r.Context().Value(http.ServerContextKey).(*http.Server); server != nil {
        fmt.Fprintf(w, "服务器地址: %s\n", server.Addr)
    }
}

func main() {
    http.HandleFunc("/", handler)
    
    // 创建监听器
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    
    // 显示监听器信息(这只是服务器绑定的地址)
    fmt.Printf("服务器监听在: %s\n", listener.Addr().String())
    
    // 启动服务器
    http.Serve(listener, nil)
}

对于更复杂的情况,可以使用realip包的增强版本:

package main

import (
    "fmt"
    "net/http"
    
    "github.com/tomasen/realip"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // 使用realip包
    clientIP := realip.FromRequest(r)
    fmt.Fprintf(w, "客户端IP (realip包): %s\n", clientIP)
    
    // 也可以自定义可信代理
    realip.SetTrustedProxies([]string{"10.0.0.0/8", "192.168.0.0/16"})
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

关键点:

  1. net.Listener.Addr()返回的是服务器自身的绑定地址,不是客户端IP
  2. 直接连接的客户端IP可以通过r.RemoteAddr获取
  3. 经过代理时需要检查X-Forwarded-ForX-Real-IP等头部
  4. realip.FromRequest已经处理了这些逻辑,是更可靠的选择

在生产环境中,推荐使用realip包或实现类似的逻辑,因为它能正确处理代理链中的IP地址。

回到顶部