f3e3e82f52
- 新增设备持久化定时任务,每5分钟将Redis中采集的设备信息同步数据库 - 新增UserDevice和UserActive模型及相应数据库表和操作接口 - 设备信息上报中获取客户端公网IP,替代原X-Forwarded-For头部 - 新建OTA功能模块,实现OTA固件升级信息查询接口 - 支持OTA黑名单过滤与定向升级设备判断 - 设计OTA相关数据库结构:ota、black_list、ota_target_device表 - 缓存预热新增按品牌分组预热型号缓存 - 依赖注入新增OTA Repository及Handler路由配置 - 实现客户端公网IP提取逻辑,支持多种代理头部优先级识别
84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
package encode
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ClientPublicIP 按优先级从多个来源获取客户端公网 IP
|
|
// 优先级:CF-Connecting-IP > X-Real-IP > X-Forwarded-For 第一个 > gin.ClientIP() 兜底
|
|
func ClientPublicIP(c *gin.Context) string {
|
|
// 1. CloudFlare 真实 IP
|
|
if cfIP := strings.TrimSpace(c.GetHeader("CF-Connecting-IP")); cfIP != "" {
|
|
if ip := parseIP(cfIP); ip != "" {
|
|
return ip
|
|
}
|
|
}
|
|
|
|
// 2. Nginx 等代理设置的真实 IP
|
|
if realIP := strings.TrimSpace(c.GetHeader("X-Real-IP")); realIP != "" {
|
|
if ip := parseIP(realIP); ip != "" {
|
|
return ip
|
|
}
|
|
}
|
|
|
|
// 3. X-Forwarded-For:取第一个(最原始的客户端 IP)
|
|
if xff := strings.TrimSpace(c.GetHeader("X-Forwarded-For")); xff != "" {
|
|
// X-Forwarded-For: client, proxy1, proxy2
|
|
parts := strings.SplitN(xff, ",", 2)
|
|
if ip := parseIP(strings.TrimSpace(parts[0])); ip != "" {
|
|
return ip
|
|
}
|
|
}
|
|
|
|
// 4. Gin 内置兜底(会按 TrustedProxies 配置解析)
|
|
return parseIP(c.ClientIP())
|
|
}
|
|
|
|
// parseIP 从 host:port 或纯 IP 字符串中提取合法的 IP 地址
|
|
func parseIP(addr string) string {
|
|
// 尝试解析为 host:port
|
|
if host, _, err := net.SplitHostPort(addr); err == nil {
|
|
addr = host
|
|
}
|
|
|
|
ip := net.ParseIP(addr)
|
|
if ip == nil {
|
|
return ""
|
|
}
|
|
|
|
// 过滤内网地址,只返回公网 IP
|
|
if !isPrivateIP(ip) {
|
|
return addr
|
|
}
|
|
|
|
// 内网地址也返回(开发环境或代理内网转发场景)
|
|
return addr
|
|
}
|
|
|
|
// isPrivateIP 判断是否为内网/保留 IP
|
|
func isPrivateIP(ip net.IP) bool {
|
|
privateRanges := []struct {
|
|
cidr string
|
|
}{
|
|
{"10.0.0.0/8"},
|
|
{"172.16.0.0/12"},
|
|
{"192.168.0.0/16"},
|
|
{"127.0.0.0/8"},
|
|
{"169.254.0.0/16"},
|
|
{"::1/128"},
|
|
{"fc00::/7"},
|
|
{"fe80::/10"},
|
|
}
|
|
|
|
for _, r := range privateRanges {
|
|
_, network, _ := net.ParseCIDR(r.cidr)
|
|
if network.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|