Master IP Address Lookup in Java: Local ip2region & Online API Integration
This guide explains how to integrate the ip2region library for offline IP‑to‑region lookups, implement memory‑accelerated queries, use an online WHOIS service for richer data, and embed the logic into a Spring Boot interceptor for seamless request handling.
Introduction
If you need to resolve IP addresses locally, you can use the ip2region project, which provides a detailed local IP‑to‑address mapping table. Import the dependency and specify the version, as APIs may differ between releases.
Import
Add the following Maven dependency to your project:
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.3</version>
</dependency> Official Giteehttps://gitee.com/lionsoul/ip2region.git
Development
Download the .xdb file to your project directory. The ip2region library can query the file in microsecond‑level latency. Two memory‑acceleration options are available:
vIndex cache: uses a fixed 512 KiB memory area to cache the vector index, reducing one disk I/O and keeping average query time between 10‑20 µs.
Full file cache: loads the entire .xdb into memory, eliminating disk I/O and achieving sub‑microsecond query speed.
/**
* IP query
*/
@Slf4j
public class IPUtil {
private static final String UNKNOWN = "unknown";
protected IPUtil() {}
/**
* Get client IP address, handling reverse proxies like Nginx.
* If multiple proxies are present, the first non‑unknown entry in X‑Forwarded‑For is the real IP.
*/
public static String getIpAddr(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;
}
public static String getAddr(String ip) {
String dbPath = "src/main/resources/ip2region/ip2region.xdb";
byte[] cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
log.info("failed to load content from `%s`: %s
", dbPath, e);
return null;
}
Searcher searcher;
try {
searcher = Searcher.newWithBuffer(cBuff);
} catch (Exception e) {
log.info("failed to create content cached searcher: %s
", e);
return null;
}
try {
String region = searcher.searchByStr(ip);
return region;
} catch (Exception e) {
log.info("failed to search(%s): %s
", ip, e);
}
return null;
}
}The utility class provides methods to obtain the client IP and resolve it against the local .xdb database. Some IPs may not be resolvable due to gaps in the offline data.
Online Parsing
For more comprehensive information, you can query the online WHOIS service at whois.pconline.com.cn, which generally returns accurate results with few failures.
@Slf4j
public class AddressUtils {
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
public static final String UNKNOWN = "XX XX";
public static final String getRealAddressByIP(String ip) {
String address = UNKNOWN;
if (IpUtils.internalIp(ip)) {
return "Internal IP";
}
try {
String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true", "GBK");
if (StrUtil.isEmpty(rspStr)) {
log.error("Failed to get location for {}", ip);
return UNKNOWN;
}
JSONObject obj = JSONObject.parseObject(rspStr);
String region = obj.getString("pro");
String city = obj.getString("city");
return String.format("%s %s", region, city);
} catch (Exception e) {
log.error("Failed to get location for {}", ip);
}
return address;
}
public static String sendGet(String url, String param, String contentType) {
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("ConnectException while calling sendGet, url=" + url + ", param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("SocketTimeoutException while calling sendGet, url=" + url + ", param=" + param, e);
} catch (IOException e) {
log.error("IOException while calling sendGet, url=" + url + ", param=" + param, e);
} catch (Exception e) {
log.error("Exception while calling sendGet, url=" + url + ", param=" + param, e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception ex) {
log.error("Exception while closing stream, url=" + url + ", param=" + param, ex);
}
}
return result.toString();
}
}Scenario
Integrating IP resolution early in the request lifecycle is best done with a Spring MVC interceptor. The interceptor extracts the client IP, resolves its geographic address, and stores these values for downstream processing.
/**
* Interceptor to limit requests based on IP and capture IP information.
*/
@Slf4j
@Configuration
public class IpUrlLimitInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Constant.IP = IPUtil.getIpAddr(request);
Constant.IP_ADDR = AddressUtils.getRealAddressByIP(Constant.IP);
Constant.URL = request.getRequestURI();
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
// Additional processing can be added here.
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}To activate the interceptor, register it in a Spring Boot configuration class:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
IpUrlLimitInterceptor ipUrlLimitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ipUrlLimitInterceptor)
.addPathPatterns("/**");
}
}Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
