如何从http.Request
中获取所有客户端的IP地址?在PHP
中,有很多需要检查的变量。在Go语言中也是这样吗?
我找到的一个方法是:
req.RemoteAddr
请求是否区分大小写?例如x-forwarded-for
和X-Forwarded-For
以及X-FORWARDED-FOR
是相同的吗?(来自req.Header.Get("X-FORWARDED-FOR")
)
查看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
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
会为您规范化键名。(如果您想直接访问header map,而不是通过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)
}
将产生:
10.0.0.1
10.0.0.2
10.0.0.3
这是我构想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 获取
X-Forwarded-For
或 X-Real-IP
标头为任意值。除非您有可信赖的反向代理,否则不应使用这些值。" - hewiefreeman这里是一个完全工作的例子
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))
}
我认为我有比当前发布的方法更好的方法。
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))
}
}
}
X-Forwarded-For: <client>, <proxy1>, <proxy2>
例如,有人可能会这样调用:curl -H "X-Forwarded-For: 136.226.254.1" -H "X-Real-Ip: 136.226.254.2" "http://super.com"
例如,nginx模块http://nginx.org/ru/docs/http/ngx_http_realip_module.html使用基于获取链式X-Forwarded-For地址中最后一个不受信任的IP地址的逻辑。我没有找到合适的中间件来使用相同的逻辑,并编写了它:https://github.com/thrownew/go-middlewares/tree/main/clientip
在使用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
)
}
req.Header
的文档阅读中得出结论,你只需要执行req.Header.Get("X-Forwarded-For")
就可以了,因为其他情况都已经被解析器规范化到此处了。 - Lars Haugsethhttp.CanonicalHeaderKey
而不是尝试所有可能的大写和小写组合。 - bithavoc