引入pom.xml
<!-- ip2region 离线IP归属地查询 -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>并且下载xdb文件 https://github.com/lionsoul2014/ip2region/tree/master/data
创建工具类
package com.tb.tbsurvey.common.util;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import java.io.InputStream;
/**
* IP 工具类:获取客户端真实 IP 及归属地查询
*/
@Slf4j
@Component
public class IpUtils {
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST_IPV4 = "127.0.0.1";
private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
private Searcher searcher;
@PostConstruct
public void init() {
try {
ClassPathResource resource = new ClassPathResource("ip2region/ip2region_v4.xdb");
InputStream inputStream = resource.getInputStream();
byte[] dbBuff = FileCopyUtils.copyToByteArray(inputStream);
// 使用完全基于内存的查询(并发安全)
searcher = Searcher.newWithBuffer(dbBuff);
log.info("ip2region 数据库加载成功");
} catch (Exception e) {
log.warn("ip2region 数据库加载失败,IP归属地功能不可用: {}", e.getMessage());
}
}
/**
* 从 HttpServletRequest 获取客户端真实 IP
* 处理代理情况:X-Forwarded-For、X-Real-IP 等
*/
public String getClientIp(HttpServletRequest request) {
if (request == null) {
return "";
}
String ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.hasText(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (!StringUtils.hasText(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (!StringUtils.hasText(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (!StringUtils.hasText(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (!StringUtils.hasText(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (!StringUtils.hasText(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// X-Forwarded-For 可能包含多个 IP,取第一个(真实客户端 IP)
if (StringUtils.hasText(ip) && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
// 处理本地回环地址
if (LOCALHOST_IPV6.equals(ip)) {
ip = LOCALHOST_IPV4;
}
return ip;
}
/**
* 获取 IP 归属地
* @return 格式如:浙江-温州,未知返回空字符串
*/
public String getIpLocation(String ip) {
if (!StringUtils.hasText(ip) || searcher == null) {
return "";
}
// 本地 IP 不查询
if (LOCALHOST_IPV4.equals(ip) || ip.startsWith("192.168.") || ip.startsWith("10.") || ip.startsWith("172.")) {
return "内网IP";
}
try {
// ip2region 返回格式:国家|区域|省份|城市|ISP
// 例如:中国|0|浙江省|温州市|电信
String region = searcher.search(ip);
return parseLocation(region);
} catch (Exception e) {
log.warn("IP归属地查询失败: ip={}, error={}", ip, e.getMessage());
return "";
}
}
/**
* 解析 ip2region 返回的地区信息
* 输入:中国|0|浙江省|温州市|电信
* 输出:浙江-温州
*/
private String parseLocation(String region) {
if (!StringUtils.hasText(region)) {
return "";
}
String[] parts = region.split("\\|");
if (parts.length < 4) {
return "";
}
String province = parts[2]; // 浙江省
String city = parts[3]; // 温州市
// 移除"省"、"市"、"自治区"等后缀
province = removeSuffix(province, "省", "市", "自治区", "特别行政区");
city = removeSuffix(city, "市", "县", "区");
// 如果是直辖市或省和市相同,只返回省份
if ("0".equals(city) || !StringUtils.hasText(city) || province.equals(city)) {
return StringUtils.hasText(province) && !"0".equals(province) ? province : "";
}
// 如果省份为0(未知),只返回城市
if ("0".equals(province) || !StringUtils.hasText(province)) {
return StringUtils.hasText(city) && !"0".equals(city) ? city : "";
}
return province + "-" + city;
}
/**
* 移除字符串的指定后缀
*/
private String removeSuffix(String str, String... suffixes) {
if (!StringUtils.hasText(str)) {
return str;
}
for (String suffix : suffixes) {
if (str.endsWith(suffix)) {
return str.substring(0, str.length() - suffix.length());
}
}
return str;
}
/**
* 获取格式化的 IP 及归属地
* @return 格式如:61.164.138.58(浙江-温州)
*/
public String getIpWithLocation(HttpServletRequest request) {
String ip = getClientIp(request);
if (!StringUtils.hasText(ip)) {
return "";
}
String location = getIpLocation(ip);
if (StringUtils.hasText(location)) {
return ip + "(" + location + ")";
}
return ip;
}
/**
* 获取 User-Agent
*/
public String getUserAgent(HttpServletRequest request) {
if (request == null) {
return "";
}
String ua = request.getHeader("User-Agent");
// 限制长度,避免超长 UA
if (ua != null && ua.length() > 500) {
ua = ua.substring(0, 500);
}
return ua;
}
}