164 lines
3.4 KiB
Go
164 lines
3.4 KiB
Go
/**
|
||
* package utils
|
||
* 作用:IP归属地查询工具
|
||
* 说明:使用第三方API查询IP归属地信息
|
||
*/
|
||
package utils
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// IPLocationInfo IP归属地信息
|
||
type IPLocationInfo struct {
|
||
Country string `json:"country"` // 国家
|
||
Region string `json:"region"` // 省份
|
||
City string `json:"city"` // 城市
|
||
ISP string `json:"isp"` // 运营商
|
||
FullLocation string `json:"full_location"` // 完整归属地
|
||
}
|
||
|
||
// 缓存结构(简单内存缓存)
|
||
var (
|
||
ipCache = make(map[string]*IPLocationInfo)
|
||
cacheTimeout = 24 * time.Hour
|
||
)
|
||
|
||
/**
|
||
* GetIPLocation
|
||
* 功能:获取IP归属地信息
|
||
* @param ip IP地址
|
||
* @returns 归属地信息字符串
|
||
*/
|
||
func GetIPLocation(ip string) string {
|
||
// 排除本地IP
|
||
if isLocalIP(ip) {
|
||
return "本地"
|
||
}
|
||
|
||
// 检查缓存
|
||
if info, ok := ipCache[ip]; ok {
|
||
return info.FullLocation
|
||
}
|
||
|
||
// 查询IP归属地
|
||
info := queryIPLocation(ip)
|
||
if info != nil {
|
||
// 构建完整归属地字符串
|
||
parts := []string{}
|
||
if info.Country != "" {
|
||
parts = append(parts, info.Country)
|
||
}
|
||
if info.Region != "" {
|
||
parts = append(parts, info.Region)
|
||
}
|
||
if info.City != "" {
|
||
parts = append(parts, info.City)
|
||
}
|
||
if info.ISP != "" {
|
||
parts = append(parts, info.ISP)
|
||
}
|
||
|
||
if len(parts) > 0 {
|
||
info.FullLocation = strings.Join(parts, " ")
|
||
} else {
|
||
info.FullLocation = "未知"
|
||
}
|
||
|
||
// 存入缓存
|
||
ipCache[ip] = info
|
||
return info.FullLocation
|
||
}
|
||
|
||
return "未知"
|
||
}
|
||
|
||
/**
|
||
* isLocalIP
|
||
* 功能:判断是否为本地IP
|
||
*/
|
||
func isLocalIP(ip string) bool {
|
||
// 本地回环地址
|
||
if ip == "127.0.0.1" || ip == "localhost" || ip == "::1" {
|
||
return true
|
||
}
|
||
|
||
// 内网地址
|
||
if strings.HasPrefix(ip, "192.168.") ||
|
||
strings.HasPrefix(ip, "10.") ||
|
||
strings.HasPrefix(ip, "172.16.") ||
|
||
strings.HasPrefix(ip, "172.17.") ||
|
||
strings.HasPrefix(ip, "172.18.") ||
|
||
strings.HasPrefix(ip, "172.19.") ||
|
||
strings.HasPrefix(ip, "172.20.") ||
|
||
strings.HasPrefix(ip, "172.21.") ||
|
||
strings.HasPrefix(ip, "172.22.") ||
|
||
strings.HasPrefix(ip, "172.23.") ||
|
||
strings.HasPrefix(ip, "172.24.") ||
|
||
strings.HasPrefix(ip, "172.25.") ||
|
||
strings.HasPrefix(ip, "172.26.") ||
|
||
strings.HasPrefix(ip, "172.27.") ||
|
||
strings.HasPrefix(ip, "172.28.") ||
|
||
strings.HasPrefix(ip, "172.29.") ||
|
||
strings.HasPrefix(ip, "172.30.") ||
|
||
strings.HasPrefix(ip, "172.31.") {
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
/**
|
||
* queryIPLocation
|
||
* 功能:查询IP归属地(使用ip-api.com免费API)
|
||
*/
|
||
func queryIPLocation(ip string) *IPLocationInfo {
|
||
// 使用ip-api.com免费API(限制:每分钟45次请求)
|
||
url := fmt.Sprintf("http://ip-api.com/json/%s?lang=zh-CN&fields=status,message,country,regionName,city,isp", ip)
|
||
|
||
client := &http.Client{
|
||
Timeout: 3 * time.Second,
|
||
}
|
||
|
||
resp, err := client.Get(url)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
var result struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Country string `json:"country"`
|
||
Region string `json:"regionName"`
|
||
City string `json:"city"`
|
||
ISP string `json:"isp"`
|
||
}
|
||
|
||
if err := json.Unmarshal(body, &result); err != nil {
|
||
return nil
|
||
}
|
||
|
||
if result.Status != "success" {
|
||
return nil
|
||
}
|
||
|
||
return &IPLocationInfo{
|
||
Country: result.Country,
|
||
Region: result.Region,
|
||
City: result.City,
|
||
ISP: result.ISP,
|
||
}
|
||
}
|
||
|