golang如何获取客户端ip?

golang获取客户端ip的方法:

ClientIP方法与ClientPublicIP方法的实现类似,只是一个按照http协议约定获取客户端ip, 一个按照约定格式查找到公网ip。

在网络与服务架构、业务逻辑复杂的环境中,按照http协议约定的方式,并非总能获取到真实的ip,在我们的业务中用户流量经由三方多层级转发(都是三方自己实现的http client) ,难免会出现一些纰漏,这时越往后的服务获取用户真实ip越加困难,你甚至不知道自己获取的ip是否是真实的。

但是我们的客户经由三方转发而来的流量,那么客户极大多数甚至排除测试之外都是公网用户,结合使用 ClientPublicIP 和 ClientIP 方法总能更好的获取用户的真实ip

// var r *http.Request
ip := exnet.ClientPublicIP(r)
if ip == ""{
  ip = exnet.ClientIP(r)
}

用上面的方法总能有效的获取用户真实的ip地址,下面分析下两个方法的具体实现。

// ClientIP 尽最大努力实现获取客户端 IP 的算法。
// 解析 X-Real-IP 和 X-Forwarded-For 以便于反向代理(nginx 或 haproxy)可以正常工作。
func ClientIP(r *http.Request) string {
xForwardedFor := r.Header.Get("X-Forwarded-For")
ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0])
if ip != "" {
return ip
}
ip = strings.TrimSpace(r.Header.Get("X-Real-Ip"))
if ip != "" {
return ip
}
if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil {
return ip
}
return ""
}

ClientIP首先读取X-Forwarded-For header中用 , 分隔的第一个ip地址,如果这个地址不存在,就会从X-Real-Ip header中获取,如果还是不存在,说明流量并非是由反向代理转发而来,而是客户端直接请求服务,这时通过http.Request.RemoteAddr字段截取除去端口号的ip地址。

这个方法很简单,就是按照http约定的格式获取,其中X-Forwarded-For和X-Real-Ip header由反向代理填充,例如nginx或 haproxy。

// ClientPublicIP 尽最大努力实现获取客户端公网 IP 的算法。
// 解析 X-Real-IP 和 X-Forwarded-For 以便于反向代理(nginx 或 haproxy)可以正常工作。
func ClientPublicIP(r *http.Request) string {
var ip string
for _, ip = range strings.Split(r.Header.Get("X-Forwarded-For"), ",") {
ip = strings.TrimSpace(ip)
if ip != "" && !HasLocalIPddr(ip) {
return ip
}
}
ip = strings.TrimSpace(r.Header.Get("X-Real-Ip"))
if ip != "" && !HasLocalIPddr(ip) {
return ip
}
if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil {
if !HasLocalIPddr(ip) {
return ip
}
}
return ""
}

ClientPublicIP很简单,和ClientIP方法的读取顺序一样,只是试图中X-Forwarded-For列表中找到一个公网ip,如果没有检查X-Real-Ip 是否是一个公网ip,其次检查 http.Request.RemoteAddr 是否是公网ip,如果没有找到公网ip这返回一个空字符串。

这个方法可以让我们有机会优先获取到用户的公网 ip,往往公网 ip 对我们来说更有价值。


golang如何获取客户端ip?