66 lines
1.4 KiB
Go
66 lines
1.4 KiB
Go
package helper
|
||
|
||
import (
|
||
"net"
|
||
"strings"
|
||
|
||
"github.com/go-kratos/kratos/v2/transport/http"
|
||
)
|
||
|
||
// GetClientIP 获取客户端真实 IP
|
||
func GetClientIP(ctx http.Context) string {
|
||
|
||
// 检查 X-Forwarded-For 头(多个代理时格式为 "client, proxy1, proxy2")
|
||
if xff := ctx.Header().Get("X-Forwarded-For"); xff != "" {
|
||
ips := strings.Split(xff, ",")
|
||
for _, ip := range ips {
|
||
ip = strings.TrimSpace(ip)
|
||
if ip != "" {
|
||
// 验证是否为合法 IP
|
||
if isValidIP(ip) {
|
||
return ip
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查 X-Real-IP 头
|
||
if realIP := ctx.Header().Get("X-Real-IP"); realIP != "" {
|
||
if isValidIP(realIP) {
|
||
return realIP
|
||
}
|
||
}
|
||
|
||
// 检查 X-Forwarded
|
||
if forwarded := ctx.Header().Get("X-Forwarded"); forwarded != "" {
|
||
// 格式可能为 "for=client-ip;host=example.com;proto=https"
|
||
parts := strings.Split(forwarded, ";")
|
||
for _, part := range parts {
|
||
part = strings.TrimSpace(part)
|
||
if strings.HasPrefix(part, "for=") {
|
||
ip := strings.TrimPrefix(part, "for=")
|
||
ip = strings.Trim(ip, `"`) // 可能被引号包围
|
||
if isValidIP(ip) {
|
||
return ip
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 直接从 RemoteAddr 获取
|
||
remoteAddr := ctx.Request().RemoteAddr
|
||
if ip, _, err := net.SplitHostPort(remoteAddr); err == nil {
|
||
if isValidIP(ip) {
|
||
return ip
|
||
}
|
||
}
|
||
|
||
return ""
|
||
}
|
||
|
||
// 验证是否为合法 IP
|
||
func isValidIP(ip string) bool {
|
||
parsedIP := net.ParseIP(ip)
|
||
return parsedIP != nil
|
||
}
|