package quant.rich.emoney.util; import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.record.Subdivision; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import quant.rich.emoney.client.OkHttpClientProvider; import quant.rich.emoney.entity.config.ProxyConfig; import quant.rich.emoney.pojo.dto.IpInfo; import java.io.IOException; import java.net.InetAddress; import java.net.Proxy; import java.util.concurrent.TimeUnit; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.Async; @Slf4j public class GeoIPUtil { private static DatabaseReader cityReader; public static String LOCALE = "zh-CN"; static { try { ClassPathResource geoLite2CityResource = new ClassPathResource("/conf/extra/GeoLite2-City.mmdb"); cityReader = new DatabaseReader.Builder(geoLite2CityResource.getInputStream()).build(); } catch (IOException e) { throw new RuntimeException("IP 地址库初始化失败", e); } } @Async public static IpInfo getIpInfoThroughProxy(ProxyConfig proxyConfig) { return CallerLockUtil.tryCallWithCallerLock(() -> { Proxy proxy = proxyConfig.getProxy(); boolean ignoreHttpsVerification = proxyConfig.getIgnoreHttpsVerification(); // OkHttp 客户端配置 OkHttpClient client = OkHttpClientProvider.getInstance( proxy, ignoreHttpsVerification, builder -> builder .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS)); // 使用 httpbin.org/ip 获取当前请求的公网 IP Request requestIpv4 = new Request.Builder() .url("https://ipv4.icanhazip.com") .build(); IpInfo ipInfo = new IpInfo(); try (Response response = client.newCall(requestIpv4).execute()) { if (!response.isSuccessful()) { log.warn("Request ipv4 failed with code: {}", response.code()); return IpInfo.EMPTY; } String responseBody = response.body().string(); log.debug("Response ipv4 from proxy: {}", responseBody.trim()); ipInfo.setIp(responseBody); } catch (IOException e) { log.warn("Proxy ipv4 error: {}", e.getMessage()); return IpInfo.EMPTY; } Request requestIpv6 = new Request.Builder() .url("https://ipv6.icanhazip.com") .build(); try (Response response = client.newCall(requestIpv6).execute()) { if (!response.isSuccessful()) { log.warn("Request ipv6 failed with code: {}", response.code()); } String responseBody = response.body().string(); log.debug("Response ipv6 from proxy: {}", responseBody.trim()); ipInfo.setIpv6(responseBody); } catch (IOException e) { log.warn("Proxy ipv6 error {}", e.getMessage()); } return queryIpInfoGeoLite(ipInfo); }, 100, proxyConfig).orElse(IpInfo.EMPTY); } /** * 以 IPv4 为准 * @param ipInfo */ public static IpInfo queryIpInfoGeoLite(IpInfo ipInfo) { try { InetAddress ipv4Address = InetAddress.getByName(ipInfo.getIp()); CityResponse response = cityReader.city(ipv4Address); if (response.getCountry() != null) { String countryStr = response.getCountry().getNames().get(LOCALE); ipInfo.setCountry(countryStr); } if (response.getSubdivisions() != null && !response.getSubdivisions().isEmpty()) { // 省 Subdivision subdivision = response.getSubdivisions().get(0); if (subdivision != null) { String subdivisionStr = subdivision.getNames().get(LOCALE); ipInfo.setSubdivision(subdivisionStr); } } if (response.getCity() != null) { // 市 String cityStr = response.getCity().getNames().get(LOCALE); ipInfo.setCity(cityStr); } } catch (IOException | GeoIp2Exception e) { log.warn("Get city response from GeoLite2-City database failed: {}", e.getMessage()); } log.debug("ipInfo: {}", ipInfo); return ipInfo; } public static void main(String[] args) { System.out.format("GeoLite2-City %s\r\n", queryIpInfoGeoLite(new IpInfo().setIp("46.3.98.216"))); } }