从http.Request(Golang)获取客户端IP地址的正确方法

Kok*_*zzu 59 ip http go

获取所有客户端IP地址的正确方法是http.Request什么?在PHP有很多的变数,我应该检查.Go上也一样吗?

我发现的一个是:

req.RemoteAddr
Run Code Online (Sandbox Code Playgroud)

请求是否区分大小写?例如和?x-forwarded-for相同?(来自)X-Forwarded-ForX-FORWARDED-FORreq.Header.Get("X-FORWARDED-FOR")

tom*_*asz 70

查看http.Request,您可以找到以下成员变量:

// HTTP defines that header names are case-insensitive.
// The request parser implements this by canonicalizing the
// name, making the first character and any characters
// following a hyphen uppercase and the rest lowercase.
//
// For client requests certain headers are automatically
// added and may override values in Header.
//
// See the documentation for the Request.Write method.
Header Header

// RemoteAddr allows HTTP servers and other software to record
// the network address that sent the request, usually for
// logging. This field is not filled in by ReadRequest and
// has no defined format. The HTTP server in this package
// sets RemoteAddr to an "IP:port" address before invoking a
// handler.
// This field is ignored by the HTTP client.
RemoteAddr string
Run Code Online (Sandbox Code Playgroud)

您可以使用RemoteAddr获取远程客户端的IP地址和端口(格式为"IP:port"),这是原始请求者或最后一个代理的地址(例如,位于服务器前面的负载均衡器).

这就是你所拥有的一切.

然后,您可以调查不区分大小写的标头(上面的每个文档),这意味着您的所有示例都将起作用并产生相同的结果:

req.Header.Get("X-Forwarded-For") // capitalisation
req.Header.Get("x-forwarded-for") // doesn't
req.Header.Get("X-FORWARDED-FOR") // matter
Run Code Online (Sandbox Code Playgroud)

这是因为内部http.Header.Get会为您规范化密钥.(如果要直接访问标题映射,而不是通过Get,则需要首先使用http.CanonicalHeaderKey.)

最后,"X-Forwarded-For"可能是您想要查看的字段,以获取有关客户端IP的更多信息.这在很大程度上取决于远程端使用的HTTP软件,因为客户端可以根据需要放置任何内容.另请注意,此字段的预期格式是逗号+空格分隔的IP地址列表.您需要稍微解析它以获得您选择的单个IP(可能是列表中的第一个IP),例如:

// Assuming format is as expected
ips := strings.Split("10.0.0.1, 10.0.0.2, 10.0.0.3", ", ")
for _, ip := range ips {
    fmt.Println(ip)
}
Run Code Online (Sandbox Code Playgroud)

将产生:

10.0.0.1
10.0.0.2
10.0.0.3
Run Code Online (Sandbox Code Playgroud)

  • 是的,当然,我的回答是_all_变体将起作用,因为标题不区分大小写.我之所以提到这些,只是因为OP询问了区分大小写,但可以看到措辞可能令人困惑.会更新. (2认同)

Ste*_*ger 15

这是一个完全有效的例子

package main

import (  
    // Standard library packages
    "fmt"
    "strconv"
    "log"
    "net"
    "net/http"

    // Third party packages
    "github.com/julienschmidt/httprouter"
    "github.com/skratchdot/open-golang/open"
)



// https://blog.golang.org/context/userip/userip.go
func getIP(w http.ResponseWriter, req *http.Request, _ httprouter.Params){
    fmt.Fprintf(w, "<h1>static file server</h1><p><a href='./static'>folder</p></a>")

    ip, port, err := net.SplitHostPort(req.RemoteAddr)
    if err != nil {
        //return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)

        fmt.Fprintf(w, "userip: %q is not IP:port", req.RemoteAddr)
    }

    userIP := net.ParseIP(ip)
    if userIP == nil {
        //return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
        fmt.Fprintf(w, "userip: %q is not IP:port", req.RemoteAddr)
        return
    }

    // This will only be defined when site is accessed via non-anonymous proxy
    // and takes precedence over RemoteAddr
    // Header.Get is case-insensitive
    forward := req.Header.Get("X-Forwarded-For")

    fmt.Fprintf(w, "<p>IP: %s</p>", ip)
    fmt.Fprintf(w, "<p>Port: %s</p>", port)
    fmt.Fprintf(w, "<p>Forwarded for: %s</p>", forward)
}


func main() {  
    myport := strconv.Itoa(10002);


    // Instantiate a new router
    r := httprouter.New()

    r.GET("/ip", getIP)

    // Add a handler on /test
    r.GET("/test", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
        // Simply write some test data for now
        fmt.Fprint(w, "Welcome!\n")
    })  


    l, err := net.Listen("tcp", "localhost:" + myport)
    if err != nil {
        log.Fatal(err)
    }
    // The browser can connect now because the listening socket is open.


    //err = open.Start("http://localhost:"+ myport + "/test")
    err = open.Start("http://localhost:"+ myport + "/ip")
    if err != nil {
         log.Println(err)
    }

    // Start the blocking server loop.
    log.Fatal(http.Serve(l, r)) 
}
Run Code Online (Sandbox Code Playgroud)

  • @kristen:实际上,X-Forwarded-For(可能)是IP地址列表(代理链接)。因此,您需要先用“,”分隔。 (2认同)

小智 7

我想我有一个比当前发布的方法更好的方法。

package main

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

func main() {
    http.HandleFunc("/", getUserIP)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal(err)
    }
}

// Get the IP address of the server's connected user.
func getUserIP(httpWriter http.ResponseWriter, httpServer *http.Request) {
    var userIP string
    if len(httpServer.Header.Get("CF-Connecting-IP")) > 1 {
        userIP = httpServer.Header.Get("CF-Connecting-IP")
        fmt.Println(net.ParseIP(userIP))
    } else if len(httpServer.Header.Get("X-Forwarded-For")) > 1 {
        userIP = httpServer.Header.Get("X-Forwarded-For")
        fmt.Println(net.ParseIP(userIP))
    } else if len(httpServer.Header.Get("X-Real-IP")) > 1 {
        userIP = httpServer.Header.Get("X-Real-IP")
        fmt.Println(net.ParseIP(userIP))
    } else {
        userIP = httpServer.RemoteAddr
        if strings.Contains(userIP, ":") {
            fmt.Println(net.ParseIP(strings.Split(userIP, ":")[0]))
        } else {
            fmt.Println(net.ParseIP(userIP))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请解释为什么这样更好,你解释得越多,答案就越好。 (6认同)

Jim*_*imB 5

在 PHP 中,有很多变量需要检查。Go 上也一样吗?

这与 Go(或 PHP)无关。它仅取决于客户端、代理、负载均衡器或服务器发送的内容。根据您的环境选择您需要的。

http.Request.RemoteAddr包含远程 IP 地址。它可能是也可能不是您的实际客户。

请求是否区分大小写?例如 x-forwarded-for 与 X-Forwarded-For 和 X-FORWARDED-FOR 相同吗?(来自 req.Header.Get("X-FORWARDED-FOR"))

不,为什么不自己尝试一下呢?http://play.golang.org/p/YMf_UBvDsH


mel*_*ngs 5

这就是我提出IP的方式

func ReadUserIP(r *http.Request) string {
    IPAddress := r.Header.Get("X-Real-Ip")
    if IPAddress == "" {
        IPAddress = r.Header.Get("X-Forwarded-For")
    }
    if IPAddress == "" {
        IPAddress = r.RemoteAddr
    }
    return IPAddress
}
Run Code Online (Sandbox Code Playgroud)
  • X-Real-Ip-获取第一个真实IP(如果请求位于多个NAT源/负载均衡器之后)

  • X-Forwarded-For-如果出于某些原因X-Real-Ip为空并且不返回响应,请从X-Forwarded-For获取

  • 远程地址-不得已(通常是不可靠的,因为这可能是最后一个IP,或者如果它是对服务器的裸http请求,即没有负载均衡器)

  • 警告:检查对此答案的评论 - /sf/answers/3905331/ - “客户端可以将 `X-Forwarded-For` 或 `X-Real-IP` 标头设置为任意值它需要。除非你有一个受信任的反向代理,否则你不应该使用任何这些值。” (5认同)