Backend Development 12 min read

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.

Top Architect
Top Architect
Top Architect
Implementing IP Geolocation in Spring Boot Using ip2region (Local and Online Methods)

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.

backendJavaSpring BootInterceptorIP Geolocationip2region
Top Architect
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.