You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot获取请求方地理位置遇GeoLite2报错,求解决方案

解决Spring Boot中GeoLite2获取内网/本地IP地理位置异常及替代方案

一、当前GeoLite2异常的原因

GeoLite2的City数据库只存储公网IP的地理映射信息,以下类型IP不在数据库中:

  • 本地回环IP:127.0.0.1、::1
  • 内网私有IP:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16
    直接调用dbReader.city()会抛出"The address xxx is not in the database"异常。

二、修改GeoLite2代码解决问题

1. 先判断IP类型,跳过无效IP查询

添加IP类型判断逻辑,同时处理CityResponse中可能为null的字段,避免空指针:

import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.model.CityResponse;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;

@Component
public class GeoLocationService {
    private static final Pattern PRIVATE_IP_PATTERN = Pattern.compile(
            "^(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.|127\\.)");

    @Resource
    private ResourceLoader resourceLoader;

    public String getGeoLocation(String ipAddress) {
        // 先过滤无效IP
        if (isPrivateOrLoopbackIp(ipAddress)) {
            return "无法获取内网/本地IP的地理位置信息";
        }

        StringBuilder geoLocation = new StringBuilder();
        try {
            // 用ResourceLoader加载文件,适配jar包运行场景
            Resource resource = resourceLoader.getResource("classpath:GeoLite2-City/GeoLite2-City.mmdb");
            File database = resource.getFile();
            DatabaseReader dbReader = new DatabaseReader.Builder(database).build();
            CityResponse response = dbReader.city(InetAddress.getByName(ipAddress));

            // 处理可能为null的字段
            String countryName = response.getCountry() != null ? response.getCountry().getName() : "未知";
            String cityName = response.getCity() != null ? response.getCity().getName() : "未知";
            String postal = response.getPostal() != null ? response.getPostal().getCode() : "未知";
            String state = response.getLeastSpecificSubdivision() != null ? response.getLeastSpecificSubdivision().getName() : "未知";

            geoLocation.append("Country: ").append(countryName)
                    .append(" City: ").append(cityName)
                    .append(" Postal: ").append(postal)
                    .append(" State: ").append(state);

            System.out.println(geoLocation);
        } catch (UnknownHostException e) {
            return "无效的IP地址格式";
        } catch (IOException e) {
            return "GeoLite2数据库加载失败,请检查文件路径或完整性";
        } catch (Exception ex) {
            return "无法获取该IP的地理位置信息";
        }
        return geoLocation.toString();
    }

    // 判断是否为内网或回环IP
    private boolean isPrivateOrLoopbackIp(String ipAddress) {
        if (ipAddress == null || ipAddress.isEmpty()) {
            return true;
        }
        return PRIVATE_IP_PATTERN.matcher(ipAddress).find() || "127.0.0.1".equals(ipAddress) || "::1".equals(ipAddress);
    }
}

2. GeoLite2文件注意事项

  • 不需要额外文件,只需确保GeoLite2-City.mmdb是从MaxMind官网下载的最新版本,文件未损坏。
  • 若应用打包为jar包,ResourceUtils.getFile()可能报错,改用ResourceLoader加载更可靠。

三、其他获取用户地理位置的方案

1. 前端浏览器Geolocation API

直接通过浏览器获取用户经纬度,再转换为城市/国家信息,精度更高但需要用户授权:

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(position => {
        const lat = position.coords.latitude;
        const lon = position.coords.longitude;
        // 调用经纬度转地理信息的接口(自行实现或用第三方服务)
    });
}

2. 第三方IP地理API

不想维护本地数据库的话,可以用第三方API:

  • MaxMind Web服务:需注册获取API密钥,支持高精度查询
  • IPinfo:提供免费/付费套餐,返回结构化地理信息
  • IP2Location:多种查询方式,支持多语言

3. 真实IP获取注意事项

如果应用部署在反向代理(Nginx、Apache)后,httpServletRequest.getRemoteAddr()拿到的是代理IP,需从请求头获取真实用户IP:

public String getRealIp(HttpServletRequest request) {
    String ip = request.getHeader("X-Forwarded-For");
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("X-Real-IP");
    }
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr();
    }
    // 处理X-Forwarded-For多IP场景,取第一个原始IP
    if (ip != null && ip.contains(",")) {
        ip = ip.split(",")[0].trim();
    }
    return ip;
}

内容的提问来源于stack exchange,提问作者Eatsam ul haq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:24:31