让防御节点正确透传用户IP并在Go服务中安全读取,具体方法如下:
一、核心解决步骤:配置防御节点透传用户真实IP
这是前提,需先让防御节点在转发请求时,将用户源IP写入 X-Forwarded-For 或 X-Real-IP 请求头,再传递给你的Go服务。
配置路径类似,通用流程如下:
1. 确认透传的请求头字段(推荐同时启用 X-Forwarded-For 和 X-Real-IP ,兼容性更强)。
二、Go服务端读取真实IP(配置防御节点后操作)
防御节点配置完成后,Go服务可通过标准库 net/http 读取请求头中的IP,代码示例如下:
1. 直接读取指定请求头
适用于已确认防御节点仅透传单个字段(如 X-Real-IP )的场景:
go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 1. 优先读取X-Real-IP(部分防御节点用此字段)
realIP := r.Header.Get("X-Real-IP")
if realIP == "" {
// 2. 若X-Real-IP为空,读取X-Forwarded-For(多个IP时取第一个,即用户源IP)
realIP = r.Header.Get("X-Forwarded-For")
}
// 3. 若仍为空, fallback 到请求的远程地址(此时是防御节点IP)
if realIP == "" {
realIP = r.RemoteAddr
}
fmt.Fprintf(w, "用户真实IP: %s", realIP)
})
http.ListenAndServe(":8080", nil)
}
2. 处理 X-Forwarded-For 多IP场景
X-Forwarded-For 格式通常为 用户IP, 防御节点IP1, 防御节点IP2 ,需提取第一个IP作为用户源IP:
go
import "strings"
func getRealIP(r *http.Request) string {
// 处理X-Forwarded-For,分割后取第一个非空IP
xff := r.Header.Get("X-Forwarded-For")
if xff != "" {
// 按逗号分割,取第一个IP并去除空格
ips := strings.Split(strings.TrimSpace(xff), ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
}
}
// fallback 到X-Real-IP和RemoteAddr
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
return realIP
}
return r.RemoteAddr
}
三、关键注意事项:过滤伪造IP(安全必做)
直接信任请求头存在风险(客户端可能伪造 X-Forwarded-For ),必须仅信任防御节点IP传递的请求头,步骤如下:
1. 从防御节点厂商获取其“回源IP段列表”(如阿里云安全回源IP段)。
2. 在Go服务中添加校验:仅当请求的 RemoteAddr (即防御节点IP)在官方IP段内时,才读取 X-Forwarded-For / X-Real-IP ,否则直接拒绝或使用 RemoteAddr 。
示例代码(简化版IP段校验):
go
// 假设已从防御节点厂商获取的回源IP段(示例,需替换为真实IP段)
var defenseNodeIPRanges = []string{"103.232.110.0/22", "180.101.49.0/24"}
// 校验IP是否在防御节点回源IP段内(实际需用net包解析CIDR)
func isDefenseNodeIP(ip string) bool {
// 真实场景需用github.com/icza/netx等库解析CIDR并校验,此处简化逻辑
for _, cidr := range defenseNodeIPRanges {
if strings.Contains(cidr, ip[:strings.LastIndex(ip, ".")]) {
return true
}
}
return false
}
func getSafeRealIP(r *http.Request) string {
// 1. 先获取请求的远程IP(防御节点IP)
defenseIP := strings.Split(r.RemoteAddr, ":")[0] // 提取IP(去除端口)
// 2. 仅信任防御节点IP传递的请求头
if isDefenseNodeIP(defenseIP) {
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
return strings.TrimSpace(strings.Split(xff, ",")[0])
}
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
return realIP
}
}
// 非防御节点请求,直接返回远程IP(可能是客户端IP或恶意请求)
return defenseIP
}
四、排查步骤(若配置后仍无法获取)
1. 验证防御节点是否透传头:用 curl 模拟防御节点请求(或通过防御节点日志),检查请求是否携带 X-Forwarded-For / X-Real-IP 。
示例: curl -H "X-Forwarded-For: 21.23.44.78" http://你的域名 ,在Go服务中打印 r.Header 确认头是否存在。
2. 检查Go服务是否被反向代理二次拦截:若Go服务前还有Nginx等反向代理,需确保Nginx也配置了“转发请求头”(如 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; )。
3. 确认防御节点配置生效:部分防御节点需清空缓存或等待配置同步,可通过更换设备访问(避免本地缓存)测试。
总结:核心是先让防御节点正确透传IP头,再在Go服务中安全读取,二者配合才能有效获取用户真实IP。