Implementing IP Geolocation in Spring Boot Using ip2region (Local and Online Methods)
This article explains how to integrate IP geolocation into a Spring Boot application by using the ip2region library for offline lookups and an online API for real‑time address resolution, providing code samples, dependency setup, caching strategies, and an interceptor to attach IP information to every request.
To display IP geolocation in a Spring Boot project, you can use the ip2region library for offline lookup or an online service such as whois.pconline.com.cn.
Dependencies
Include the ip2region Maven dependency (version 2.7.0) in your pom.xml :
<!-- ip库-->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>Download the .xdb file to your project resources. ip2region can query the file entirely from memory, achieving single‑query response times in the microsecond range. Two caching options are available:
Vector index cache (fixed 512 KiB) to reduce one disk I/O.
Full file cache (memory usage equals file size) for pure in‑memory queries.
Example utility class for IP lookup:
/**
* ip查询
*/
@Slf4j
public class IPUtil {
private static final String UNKNOWN = "unknown";
protected IPUtil() {}
/**
* 获取 IP地址
* 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
* X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实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";
// 1、从 dbPath 加载整个 xdb 到内存。
byte[] cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
log.info("failed to load content from `%s`: %s\n", dbPath, e);
return null;
}
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
Searcher searcher;
try {
searcher = Searcher.newWithBuffer(cBuff);
} catch (Exception e) {
log.info("failed to create content cached searcher: %s\n", e);
return null;
}
// 3、查询
try {
String region = searcher.searchByStr(ip);
return region;
} catch (Exception e) {
log.info("failed to search(%s): %s\n", ip, e);
}
return null;
}
}Online Lookup
If the offline database is outdated, you can query the online API at http://whois.pconline.com.cn/ipJson.jsp to obtain province and city information.
@Slf4j
public class AddressUtils {
// IP地址查询
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
// 未知地址
public static final String UNKNOWN = "XX XX";
public static String getRealAddressByIP(String ip) {
String address = UNKNOWN;
// 内网不查询
if (IpUtils.internalIp(ip)) {
return "内网IP";
}
try {
String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true", "GBK");
if (StrUtil.isEmpty(rspStr)) {
log.error("获取地理位置异常 {}", 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("获取地理位置异常 {}", 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("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception ex) {
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
}Interceptor Integration
Create a Spring MVC interceptor that extracts the client IP, resolves its address, and stores them in a global constant for later use.
/**
* 对ip 进行限制,防止IP大量请求
*/
@Slf4j
@Configuration
public class IpUrlLimitInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
// 更新全局变量
Constant.IP = IPUtil.getIpAddr(httpServletRequest);
Constant.IP_ADDR = AddressUtils.getRealAddressByIP(Constant.IP);
Constant.URL = httpServletRequest.getRequestURI();
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
// 可在此记录日志或进行其他处理
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {}
}Register the interceptor in a WebMvcConfigurer implementation so it applies to all incoming requests.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
IpUrlLimitInterceptor ipUrlLimitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ipUrlLimitInterceptor)
.addPathPatterns("/**"); // 拦截所有请求
}
}With this configuration each request will have its IP and resolved location attached, enabling features such as rate limiting, logging, or location‑based analytics.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.