BlogX项目Go-gin--根据IP获取地理位置

发布于:2025-06-26 ⋅ 阅读:(19) ⋅ 点赞:(0)

先定义一个函数来判断IP地址是否为内网,归为工具类

// utils/ip/enter.go
package ip

import "net"

func HasLocalIPAddr(ip string) bool {
	return HasLocalIP(net.ParseIP(ip))
}

// HasLocalIP 检测 IP 地址是否是内网地址
// 通过直接对比ip段范围效率更高
func HasLocalIP(ip net.IP) bool {
	if ip.IsLoopback() {
		return true
	}
// 	• ip.IsLoopback() 内置判断 IP 是否属于 127.0.0.0/8(IPv4 回环)或 ::1(IPv6)。
// • 回环属于“本机”地址,也算内网,所以直接 true。

	ip4 := ip.To4()// 若 IP 是 IPv4,返回 4 字节切片
	if ip4 == nil {
		return false// 如果不是 IPv4,就当作“不是内网 IPv4”直接 false
	}

	return ip4[0] == 10 || // 10.0.0.0/8
		(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
		(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
		(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
}

HasLocalIP——核心判断函数

• 入参:ip net.IP(字节切片),已经是结构化类型;
• 返回 bool:是内网返回 true,否则 false。

检测本地回环地址

• ip.IsLoopback() 内置判断 IP 是否属于 127.0.0.0/8(IPv4 回环)或 ::1(IPv6)。
• 回环属于“本机”地址,也算内网,所以直接 true。
Apply

取 IPv4 形式

• ip.To4() 若 IP 是 IPv4,返回 4 字节切片;如果是 IPv6 或格式无效,则返回 nil。
• 如果不是 IPv4,就当作“不是内网 IPv4”直接 false(简化处理;也可以扩展 IPv6 私网段)。

判断第一类私网:10.0.0.0/8

• 私网段 A 类;只需检查 首字节=10。

判断第二类私网:172.16.0.0/12

• 首字节=172 且第二字节 16~31 范围内。

判断第三类:169.254.0.0/16

• 这是 链接本地地址(Link-local),自动分配的内网段。
Apply

判断第四类:192.168.0.0/16

• 家用路由器最常见的私网段:首字节 192,第二字节 168。
• 整个 return 语句用逻辑 OR || 把四种条件连起来——任何一类匹配即返回 true。

HasLocalIPAddr——对外友好函数

• 入参:ip string(字符串形式的 IP,如 “192.168.1.5”)。
• net.ParseIP(ip) 把字符串转换为 net.IP 类型;如果格式不对会返回 nil。
• 然后直接调用下方的 HasLocalIP 做真正判断。
• 好处:对外提供 字符串版本 API,内部使用更强大的 net.IP API。
🌟 文件作用总结
utils/ip/enter.go 提供两层接口,专门判断“某个 IP 地址是否属于内网”:
HasLocalIPAddr(ip string) bool
输入 IP 字符串,内部转成 net.IP,再复用核心逻辑。
方便外部调用。
HasLocalIP(ip net.IP) bool
更底层,直接接受 net.IP。
通过四类私网段 + 回环地址判断是否是本地/私有 IP。
采用位段对比,效率比逐条 CIDR 解析更高。
在其他模块里,你就可以:

if ipUtils.HasLocalIPAddr("192.168.1.7") {
    fmt.Println("这是内网地址")
}

init_ip_db.go

执行查询并返回查询结果

// core/init_ip_db.go
package core

import (
	ipUtils "blogx_server/utils/ip"
	"fmt"
	"github.com/lionsoul2014/ip2region/binding/golang/xdb"
	"github.com/sirupsen/logrus"
	"strings"
)
//xdb.Searcher 来自第三方库 github.com/lionsoul2014/ip2region/binding/golang/xdb,
//是官方提供的“查询器”结构体。
var searcher *xdb.Searcher
//之后要用searcher来“搜索”IP 所属地区
//在 core 包里声明了一个全局指针变量 searcher,类型是 ip2region 库的 Searcher。
//它初始为 nil,在程序启动时由 InitIPDB() 加载真实 IP 数据库后赋值,用来让其他函数高效、统一地进行 IP 地理位置查询。

func InitIPDB() {
	var dbPath = "init/ip2region.xdb"
	_searcher, err := xdb.NewWithFileOnly(dbPath)
	//只传文件路径 就创建 Searcher,它会把整个 xdb 文件一次性 mmap/加载到内存 → 查询速度快
	if err != nil {
		logrus.Fatalf("ip地址数据库加载失败 %s", err)
		return
	}
	searcher = _searcher//这里真正赋值
}
// InitIPDB()
// 在程序启动时调用一次;
// 从本地 init/ip2region.xdb 文件加载 ip2region 数据库;
// 获得 *xdb.Searcher 并存进包级变量 searcher,供全局复用。

func GetIpAddr(ip string) (addr string) {
	if ipUtils.HasLocalIPAddr(ip) {
		return "内网"
	}

	region, err := searcher.SearchByStr(ip)// 正式查询
	if err != nil {
		logrus.Warnf("错误的ip地址 %s", err)
		return "异常地址"
	}
	_addrList := strings.Split(region, "|")
	if len(_addrList) != 5 {
		// 会有这个情况吗?
		logrus.Warnf("异常的ip地址 %s", ip)
		return "未知地址"
	}
	// _addrList 五个部分
	// 国家  0  省份   市   运营商
	country := _addrList[0]
	province := _addrList[2]
	city := _addrList[3]

	if province != "0" && city != "0" {
		return fmt.Sprintf("%s·%s", province, city)
	}
	if country != "0" && province != "0" {
		return fmt.Sprintf("%s·%s", country, province)
	}
	if country != "0" {
		return country
	}
	return region
}
// GetIpAddr(ip string) string
// 业务调用接口;
// 内网 IP → "内网";
// 公网 IP → 查询地理信息→按“省·市 / 国·省 / 国”优雅返回;
// 异常或未知→返回 "异常地址" / "未知地址"。

网站公告

今日签到

点亮在社区的每一天
去签到