通常公司网络是加域的
需要获取公网ip
在线公网获取位置
思想:
1获取公网ip
2根据ip获取位置
注意:由于一般生产环境比如不通外网,docker部署等。那就需要找能本地部署的工具ip2region就是一个不错的离线开源工具,只需要下载db文件即可
windows环境查看pc的公网信息:nslookup myip.opendns.com resolver1.opendns.com
在这里插入图片描述
将db文件放在了resource下读取的

package com.lcfc.agent.test;

import lombok.Cleanup;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class AccurateLocationFinder {

    public static void main(String[] args) {
        try {
            System.out.println("开始获取地理位置信息...\n");

            // 第一步:获取公网IP
            String publicIP = getPublicIP();
            if (publicIP == null) {
                System.out.println("无法获取公网IP地址,请检查网络连接");
                return;
            }

            System.out.println("✅ 获取到公网IP: " + publicIP + "\n");

            // 第二步:使用多个地理位置API查询
            Map<String, Map<String, String>> results = new HashMap<>();

            // 查询多个API服务
            results.put("ipapi", getLocationFromIpapi(publicIP));
            results.put("ip2location", getLocationFromIp2Location(publicIP));
            results.put("ipinfo", getLocationFromIpinfo(publicIP));
            results.put("ipapi_co", getLocationFromIpapiCo(publicIP));

            // 显示所有API的结果
            System.out.println("=== 各API查询结果 ===");
            for (Map.Entry<String, Map<String, String>> entry : results.entrySet()) {
                Map<String, String> location = entry.getValue();
                if (location != null && !location.isEmpty()) {
                    System.out.println("\n🔹 " + entry.getKey().toUpperCase() + ":");
                    System.out.println("   国家: " + location.getOrDefault("country", "未知"));
                    System.out.println("   省份: " + location.getOrDefault("region", "未知"));
                    System.out.println("   城市: " + location.getOrDefault("city", "未知"));
                    System.out.println("   运营商: " + location.getOrDefault("isp", "未知"));
                    System.out.println("   坐标: " + location.getOrDefault("lat", "未知") + ", " +
                        location.getOrDefault("lon", "未知"));
                }
            }

            // 第三步:智能分析最可能的位置
            System.out.println("\n=== 综合分析结果 ===");
            analyzeAndShowBestResult(results);



        } catch (Exception e) {
            System.err.println("程序执行错误: " + e.getMessage());
        }
    }
    /**
     * @param ip
     * @Description: ip获取归属地
     * @return: java.lang.String
     * @Author: sijing.chen
     * @Date: 2024/3/19 13:12
     */
    public static String getSiteByIp(String ip) throws Exception {
        Searcher searcher = null;
            try {
//                File file = ResourceUtils.getFile("classpath:ipdb/ip2region.xdb");
//                String dbPath = file.getPath();
//                searcher = Searcher.newWithFileOnly(dbPath);
                //本地环境使用相对路径是可以的,但是部署的时候是linux环境,所以使用ResponseEntity(封装HTTP响应的相关信息), 且使用ip2region这个工具(需要下载离线包放在本地路径上,没有网络也可以使用)todo 如果想要自动更新ip库可参考官网https://github.com/lionsoul2014/ip2region 或者https://gitee.com/lionsoul/ip2region
                ResponseEntity<byte[]> ip2region = getStream("ipdb/ip2region.xdb");

                searcher = Searcher.newWithBuffer(ip2region.getBody());
                System.out.println ("获取ip2region.xdb:" + searcher) ;


            } catch (Exception e) {
                e.printStackTrace();
            }

        String site = null;

                site = searcher.search(ip);

        // 输出 region ip2region的地址固定格式是:国家|区域|省份|城市|ISP,缺省的地域信息默认是0
        return site;
    }

    /**
     * @param templateName 路径
     * @Description: 获取文件byte[]
     * @return: org.springframework.http.ResponseEntity<byte [ ]>
     * @Author: sijing.chen
     * @Date: 2024/3/19 11:06
     */
    public static ResponseEntity<byte[]> getStream(String templateName) throws IOException {
        ClassPathResource classPathResource = new ClassPathResource(templateName);
        String filename = classPathResource.getFilename();
        System.out.println("获取该路径下的ip2region.xdb文件:" + filename);
        @Cleanup InputStream inputStream = classPathResource.getInputStream();
        byte[] bytes = FileCopyUtils.copyToByteArray(inputStream);
        String fileName = new String(filename.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", fileName);
        return new ResponseEntity<>(bytes, headers, HttpStatus.CREATED);
    }
    /**
     * 获取公网IP地址
     */
    private static String getPublicIP() {
        String[] ipApis = {
            "https://api.ipify.org?format=json",
            "https://icanhazip.com",
            "https://ident.me",
            "https://ipecho.net/plain"
        };

        for (String api : ipApis) {
            try {
                URL url = new URL(api);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(3000);
                conn.setReadTimeout(3000);

                if (conn.getResponseCode() == 200) {
                    Scanner scanner = new Scanner(conn.getInputStream());
                    String response = scanner.useDelimiter("\\A").next();
                    System.out.println("使用工具 " + api  +"获取公网ip,返回值是"+response);
                    scanner.close();

                    // 解析IP地址
                    String ip;
                    if (response.contains("{")) {
                        // JSON格式
                        ip = response.split("\"ip\":\"")[1].split("\"")[0];
                    } else {
                        // 纯文本格式
                        ip = response.trim();
                    }

                    if (isValidIP(ip)) {
                        System.out.println("✅ 获取到公网IP,使用的工具是: " + api  );
                        System.out.println("获取到公网ip之后使用离线ip2 :" + getSiteByIp(ip) );

                        return ip;
                    }
                }
            } catch (Exception e) {
                // 继续尝试下一个API
            }
        }
        return null;
    }

    /**
     * 从ipapi.com获取地理位置(免费版)
     */
    private static Map<String, String> getLocationFromIpapi(String ip) {
        try {
            URL url = new URL("http://ipapi.com/ip_api.php?ip=" + ip);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            if (conn.getResponseCode() == 200) {
                Scanner scanner = new Scanner(conn.getInputStream());
                String response = scanner.useDelimiter("\\A").next();
                scanner.close();

                // 解析JSON响应
                Map<String, String> result = new HashMap<>();
                String[] parts = response.split("\"");
                for (int i = 0; i < parts.length - 1; i++) {
                    if (parts[i].equals("country_name")) {
                        result.put("country", parts[i + 2]);
                    } else if (parts[i].equals("region_name")) {
                        result.put("region", parts[i + 2]);
                    } else if (parts[i].equals("city")) {
                        result.put("city", parts[i + 2]);
                    } else if (parts[i].equals("latitude")) {
                        result.put("lat", parts[i + 1].replace(":", "").replace(",", ""));
                    } else if (parts[i].equals("longitude")) {
                        result.put("lon", parts[i + 1].replace(":", "").replace(",", ""));
                    }
                }
                return result;
            }
        } catch (Exception e) {
            System.out.println("ipapi.com查询失败: " + e.getMessage());
        }
        return null;
    }

    /**
     * 从ip2location.com获取地理位置
     */
    private static Map<String, String> getLocationFromIp2Location(String ip) {
        try {
            URL url = new URL("https://api.ip2location.com/v2/?ip=" + ip + "&key=demo");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            if (conn.getResponseCode() == 200) {
                Scanner scanner = new Scanner(conn.getInputStream());
                String response = scanner.useDelimiter("\\A").next();
                scanner.close();

                Map<String, String> result = new HashMap<>();
                // 简化的解析逻辑
                if (response.contains("country_name")) {
                    String country = extractJsonValue(response, "country_name");
                    String city = extractJsonValue(response, "city_name");
                    String region = extractJsonValue(response, "region_name");

                    result.put("country", country);
                    result.put("region", region);
                    result.put("city", city);
                }
                return result;
            }
        } catch (Exception e) {
            System.out.println("ip2location查询失败: " + e.getMessage());
        }
        return null;
    }

    /**
     * 从ipinfo.io获取地理位置
     */
    private static Map<String, String> getLocationFromIpinfo(String ip) {
        try {
            URL url = new URL("https://ipinfo.io/" + ip + "/json");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            if (conn.getResponseCode() == 200) {
                Scanner scanner = new Scanner(conn.getInputStream());
                String response = scanner.useDelimiter("\\A").next();
                scanner.close();

                Map<String, String> result = new HashMap<>();
                if (response.contains("\"country\"")) {
                    result.put("country", extractJsonValue(response, "country"));
                    result.put("region", extractJsonValue(response, "region"));
                    result.put("city", extractJsonValue(response, "city"));
                    result.put("isp", extractJsonValue(response, "org"));

                    // 解析坐标
                    if (response.contains("\"loc\"")) {
                        String loc = extractJsonValue(response, "loc");
                        if (loc.contains(",")) {
                            String[] coords = loc.split(",");
                            result.put("lat", coords[0]);
                            result.put("lon", coords[1]);
                        }
                    }
                }
                return result;
            }
        } catch (Exception e) {
            System.out.println("ipinfo.io查询失败: " + e.getMessage());
        }
        return null;
    }

    /**
     * 从ipapi.co获取地理位置
     */
    private static Map<String, String> getLocationFromIpapiCo(String ip) {
        try {
            URL url = new URL("https://ipapi.co/" + ip + "/json/");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            if (conn.getResponseCode() == 200) {
                Scanner scanner = new Scanner(conn.getInputStream());
                String response = scanner.useDelimiter("\\A").next();
                scanner.close();

                Map<String, String> result = new HashMap<>();
                if (response.contains("\"country_name\"")) {
                    result.put("country", extractJsonValue(response, "country_name"));
                    result.put("region", extractJsonValue(response, "region"));
                    result.put("city", extractJsonValue(response, "city"));
                    result.put("isp", extractJsonValue(response, "org"));
                    result.put("lat", extractJsonValue(response, "latitude"));
                    result.put("lon", extractJsonValue(response, "longitude"));
                }
                return result;
            }
        } catch (Exception e) {
            System.out.println("ipapi.co查询失败: " + e.getMessage());
        }
        return null;
    }

    /**
     * 从JSON字符串中提取值
     */
    private static String extractJsonValue(String json, String key) {
        try {
            String search = "\"" + key + "\":";
            int start = json.indexOf(search) + search.length();
            if (start > search.length()) {
                int end = json.indexOf(",", start);
                if (end == -1) end = json.indexOf("}", start);
                if (end > start) {
                    String value = json.substring(start, end).trim();
                    // 去除引号
                    if (value.startsWith("\"") && value.endsWith("\"")) {
                        value = value.substring(1, value.length() - 1);
                    }
                    return value;
                }
            }
        } catch (Exception e) {
            // 忽略解析错误
        }
        return "未知";
    }

    /**
     * 智能分析最可能的位置
     */
    private static void analyzeAndShowBestResult(Map<String, Map<String, String>> results) {
        Map<String, Integer> countryCount = new HashMap<>();
        Map<String, Integer> cityCount = new HashMap<>();

        // 统计各个位置的出现次数
        for (Map<String, String> location : results.values()) {
            if (location != null) {
                String country = location.get("country");
                String city = location.get("city");

                if (!"未知".equals(country)) {
                    countryCount.put(country, countryCount.getOrDefault(country, 0) + 1);
                }
                if (!"未知".equals(city)) {
                    cityCount.put(city, cityCount.getOrDefault(city, 0) + 1);
                }
            }
        }

        // 找出最常出现的结果
        String mostLikelyCountry = findMostFrequent(countryCount);
        String mostLikelyCity = findMostFrequent(cityCount);

        System.out.println("📍 最可能的位置:");
        System.out.println("   国家: " + mostLikelyCountry);
        System.out.println("   城市: " + mostLikelyCity);

        // 显示准确度评估
        int validResults = (int) results.values().stream()
            .filter(loc -> loc != null && !loc.isEmpty())
            .count();

        System.out.println("\n📊 准确度评估:");
        System.out.println("   成功查询的API数量: " + validResults + "/" + results.size());
        System.out.println("   置信度: " + (validResults * 25) + "%");

        System.out.println("\n💡 提示: IP定位通常精确到城市级别,");
        System.out.println("   实际位置可能与显示位置有几十公里的误差");
    }

    /**
     * 找出出现次数最多的值
     */
    private static String findMostFrequent(Map<String, Integer> countMap) {
        if (countMap.isEmpty()) return "未知";

        return countMap.entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .get().getKey();
    }

    /**
     * 验证IP地址格式
     */
    private static boolean isValidIP(String ip) {
        return ip.matches("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
    }
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐