利用GeoLite2-City.mmdb实现通过IP地址获取经纬度以及该IP的所属地区

本人最近在写一个小的练手项目,想加上访问记录的功能(即获取访问者的ip地址从而获取到该访问者的经纬度,判断是在哪个地区访问的页面同时将信息插入进数据库中从而生成一份详细的访问记录)。网上很多可以实现此功能的API,例如腾讯、百度等,但是由于需要申请专属KEY,而且必须是在线访问的,所以最终选择了使用GeoLite2数据库。
GeoLite2数据库是免费的IP地理定位数据库,但是不太准确,通过IP转换成的经纬度与真实地址相比较还有一定偏差,但是GeoLite2可以离线使用,而且数据还具有丰富性,最重要的是免费。废话不多说,进入正文。

一、关于获取本机IP

网上有很多关于获取本机真实IP的代码,但是需要注意的有以下两点:

  1. 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
  2. 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址

下面直接上代码

public static String getIP(HttpServletRequest request) {
        String ip = null;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            logger.error("IPUtils ERROR ", e);
        }
        return ip;
    }

或者可以是

private static final String UNKNOWN = "unknown";

public static String getRealIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip)?"127.0.0.1":ip;
    }

二、GeoLite2-City.mmdb用法

首先简单说一下基本思路:利用new File()创建一个GeoLite2数据库实例,之后使用DatabaseReader.Builder读取GeoLite2数据库即可。
下载地址:
下面直接上代码

/**
     * 
     * @description: 获得国家 
     * @param reader GeoLite2 数据库
     * @param ip ip地址
     * @return
     * @throws Exception
     */
    public static String getCountry(DatabaseReader reader, String ip) throws Exception {
        return reader.city(InetAddress.getByName(ip)).getCountry().getNames().get("zh-CN");
    }

    /**
     * 
     * @description: 获得省份 
     * @param reader GeoLite2 数据库
     * @param ip ip地址
     * @return
     * @throws Exception
     */
    public static String getProvince(DatabaseReader reader, String ip) throws Exception {
        return reader.city(InetAddress.getByName(ip)).getMostSpecificSubdivision().getNames().get("zh-CN");
    }

    /**
     * 
     * @description: 获得城市 
     * @param reader GeoLite2 数据库
     * @param ip ip地址
     * @return
     * @throws Exception
     */
    public static String getCity(DatabaseReader reader, String ip) throws Exception {
        return reader.city(InetAddress.getByName(ip)).getCity().getNames().get("zh-CN");
    }
    
    /**
     * 
     * @description: 获得经度 
     * @param reader GeoLite2 数据库
     * @param ip ip地址
     * @return
     * @throws Exception
     */
    public static Double getLongitude(DatabaseReader reader, String ip) throws Exception {
        return reader.city(InetAddress.getByName(ip)).getLocation().getLongitude();
    }
    
    /**
     * 
     * @description: 获得纬度
     * @param reader GeoLite2 数据库
     * @param ip ip地址
     * @return
     * @throws Exception
     */
    public static Double getLatitude(DatabaseReader reader, String ip) throws Exception {
        return reader.city(InetAddress.getByName(ip)).getLocation().getLatitude();
    }
    
    public static void main(String[] args) throws Exception {
    	// String path = req.getSession().getServletContext().getRealPath("/WEB-INF/classes/GeoLite2-City.mmdb")String path = "D:/CSDN/GeoLite2-City.mmdb";
    	// 创建 GeoLite2 数据库
    	File database = new File(path);
    	// 读取数据库内容
    	DatabaseReader reader = new DatabaseReader.Builder(database).build();
    	// 访问IP
    	String ip = "222.222.226.212";
    	String site = "国家:"+GetAddress.getCountry(reader, ip) + "\n省份:" + GetAddress.getProvince(reader, ip) + "\n城市:" + GetAddress.getCity(reader, ip)+ "\n经度:" + GetAddress.getLongitude(reader, ip)+ "\n维度:" + GetAddress.getLatitude(reader, ip);
    	System.out.println(site);
	}

三、运行结果截图

根据ip获取经纬度java ip地址获取经纬度_java

四、总结

某些IP地址在GeoLite2数据库中只能查到国家和省份,或者仅能查到国家(本人测试过部分IP地址,大部分都能通过IP获取到国家、省份、城市;小部分可以获取到国家、省份;那些仅能获取到国家的IP地址所占比重则是极低的),总的来说GeoLite2数据库在获取经纬度以及地理位置信息时还是比较好用的,若对地理位置要求比较精确的话就建议选择使用腾讯、百度的在线API去实现此功能了。
希望此文可以帮助到有需要的小伙伴,若文中有错误欢迎大家评论指出,有问题也欢迎大家在评论区留言,谢谢。