获取客户的正确方法';来自http.Request的s IP地址

获取客户的正确方法';来自http.Request的s IP地址,http,go,ip,Http,Go,Ip,从http.Request获取所有客户端IP地址的正确方法是什么?在PHP中,我应该检查很多内容。在路上也一样吗 我发现的一个例子是: req.RemoteAddr 请求是否区分大小写?例如,x-forwarded-for与x-forwarded-for和x-forwarded-for相同?(从req.Header.Get(“X-FORWARDED-FOR”)) 在PHP中,有很多变量需要检查。在路上也一样吗 这与Go(或PHP)无关。它只取决于客户端、代理、负载平衡器或服务器发送的内容。根据

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

我发现的一个例子是:

req.RemoteAddr
请求是否区分大小写?例如,
x-forwarded-for
x-forwarded-for
x-forwarded-for
相同?(从
req.Header.Get(“X-FORWARDED-FOR”)

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

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

http.Request.RemoteAddr
包含远程IP地址。它可能是也可能不是你真正的客户

请求是否区分大小写?例如,x-forwarded-for与x-forwarded-for和x-forwarded-for相同吗?(从请求头Get(“X-FORWARDED-FOR”))

不,为什么不自己试试呢

查看您可以找到以下成员变量:

// 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
您可以使用
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
这是因为内部
http.Header.Get
将为您规范化密钥。(如果要直接访问标题映射,而不是通过
Get
,则需要先使用。)

最后,
“X-Forwarded-For”
可能是您想要查看的字段,以便获取有关客户端IP的更多信息。不过,这在很大程度上取决于远程端使用的HTTP软件,因为如果客户端愿意,它可以将任何内容放入其中。另外,请注意,此字段的名称是以逗号+空格分隔的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)
}
将产生:

10.0.0.1
10.0.0.2
10.0.0.3

这里有一个完全有效的例子

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)) 
}
主程序包
进口(
//标准库包
“fmt”
“strconv”
“日志”
“净额”
“net/http”
//第三方软件包
“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,“静态文件服务器”)
ip,端口,错误:=net.SplitHostPort(req.RemoteAddr)
如果错误!=零{
//返回nil,fmt.Errorf(“userip:%q不是IP:port”,req.RemoteAddr)
fmt.Fprintf(w,“用户IP:%q不是IP:端口”,请求RemoteAddr)
}
userIP:=net.ParseIP(ip)
如果userIP==nil{
//返回nil,fmt.Errorf(“userip:%q不是IP:port”,req.RemoteAddr)
fmt.Fprintf(w,“用户IP:%q不是IP:端口”,请求RemoteAddr)
返回
}
//只有通过非匿名代理访问站点时,才会定义此项
//并优先于RemoteAddr
//Header.Get不区分大小写
转发:=请求头.Get(“X-Forwarded-For”)
fmt.Fprintf(w,“IP:%s

”,IP) fmt.Fprintf(w,“端口:%s

”,端口) fmt.Fprintf(w,“转发给:%s

”,转发) } func main(){ myport:=strconv.Itoa(10002); //实例化一个新路由器 r:=httprouter.New() r、 获取(“/ip”,getIP) //在/test上添加处理程序 r、 GET(“/test”,func(w http.ResponseWriter,r*http.Request,httprouter.Params){ //现在只需编写一些测试数据 格式打印(w,“欢迎!\n”) }) l、 错误:=net.Listen(“tcp”,“localhost:+myport”) 如果错误!=零{ log.Fatal(错误) } //浏览器现在可以连接,因为侦听套接字已打开。 //错误=打开。开始(“http://localhost:“+myport+”/test”) 错误=打开。开始(“http://localhost:“+myport+”/ip”) 如果错误!=零{ log.Println(错误) } //启动阻塞服务器循环。 log.Fatal(http.service(l,r)) }
我就是这样想出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-获取第一个真实Ip(如果请求位于多个NAT源/负载平衡器后面)

  • X-Forwarded-For-如果由于某种原因X-Real-Ip为空且未返回响应,请从X-Forwarded-For获取

  • 远程地址-最后手段(通常不可靠,因为这可能是最后一个ip,或者如果是对服务器的裸http请求,即无负载平衡器)
根据Mozilla MDN:“X-Forwarded-For(XFF)头是一个事实上的标准头,用于标识客户端的原始IP地址。”

他们在文章中发表了明确的信息

使用Cloudfront时,客户端IP地址位于
x-original-forwarded-for
中。下面是一个Javascript示例

function getIp(request) {
  const { headers, connection, socket } = request
  const connectionSocket = connection && connection.socket

  return (
    (headers && headers['x-original-forwarded-for']) ||
    (connection && connection.remoteAddress) ||
    (socket && socket.remoteAddress) ||
    (connectionSocket && connectionSocket.remoteAddress) ||
    null
  )
}

为什么不在“X-Forwarded-For”上运行ParseIP呢?@kristen:实际上,X-Forwarded-For(可能)是一个IP地址列表(代理链接)。因此,您需要先按“,”进行拆分。在设置/获取之前,标头总是转换为相同的MIME格式,因此req.Header.Get参数不区分大小写。源代码按照我阅读
req.Header
文档的方式,您只需要执行
req.Header.Get(“X-Forwarded-for”)
,因为其他情况都是由解析器规范化的。是的,当然,我的回答是所有的变体都会工作,因为头是不区分大小写的。我提到这些只是因为OP询问了案件敏感性,但我可以看出措辞可能会令人困惑。Will update.yep,标题区分大小写,但是您应该使用
http.CanonicalHeaderKey
而不是尝试所有可能的大写组合