douyu8187
douyu8187
2014-12-01 18:31

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

已采纳

What's the correct way to get all client's IP Addresses from http.Request? In PHP there are a lot of variables that I should check. Is it the same on Go?

One that I found is:

req.RemoteAddr

And is the request case sensitive? for example x-forwarded-for is the same as X-Forwarded-For and X-FORWARDED-FOR? (from req.Header.Get("X-FORWARDED-FOR"))

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

4条回答

  • douxiongzhen2126 douxiongzhen2126 7年前

    Looking at http.Request you can find the following member variables:

    // 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
    

    You can use RemoteAddr to get the remote client's IP address and port (the format is "IP:port"), which is the address of the original requestor or the last proxy (for example a load balancer which lives in front of your server).

    This is all you have for sure.

    Then you can investigate the headers, which are case-insensitive (per documentation above), meaning all of your examples will work and yield the same result:

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

    This is because internally http.Header.Get will normalise the key for you. (If you want to access header map directly, and not through Get, you would need to use http.CanonicalHeaderKey first.)

    Finally, "X-Forwarded-For" is probably the field you want to take a look at in order to grab more information about client's IP. This greatly depends on the HTTP software used on the remote side though, as client can put anything in there if it wishes to. Also, note the expected format of this field is the comma+space separated list of IP addresses. You will need to parse it a little bit to get a single IP of your choice (probably the first one in the list), for example:

    // 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)
    }
    

    will produce:

    10.0.0.1
    10.0.0.2
    10.0.0.3
    
    点赞 评论 复制链接分享
  • dttl3933 dttl3933 7年前

    In PHP there are a lot of variables that I should check. Is it the same on Go?

    This has nothing to do with Go (or PHP for that matter). It just depends on what the client, proxy, load-balancer, or server is sending. Get the one you need depending on your environment.

    http.Request.RemoteAddr contains the remote IP address. It may or may not be your actual client.

    And is the request case sensitive? for example x-forwarded-for is the same as X-Forwarded-For and X-FORWARDED-FOR? (from req.Header.Get("X-FORWARDED-FOR"))

    No, why not try it yourself? http://play.golang.org/p/YMf_UBvDsH

    点赞 评论 复制链接分享
  • doujiene2845 doujiene2845 2年前

    This is how I come up with the 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
    }
    
    • X-Real-Ip - fetches first true IP (if the requests sits behind multiple NAT sources/load balancer)

    • X-Forwarded-For - if for some reason X-Real-Ip is blank and does not return response, get from X-Forwarded-For

    • Remote Address - last resort (usually won't be reliable as this might be the last ip or if it is a naked http request to server ie no load balancer)
    点赞 评论 复制链接分享
  • duanjia4817 duanjia4817 6年前

    Here a completely working example

    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!
    ")
        })  
    
    
        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)) 
    }
    
    点赞 评论 复制链接分享