原文地址
前言
ip
地址解析需要ip地址数据
以及查询工具
,所以也可以使用数据进行存储,但是我们这里就不造轮子了,直接去拿现有工具,使用ip2region,这个工具支持多种查询方法选择
- 现读现查的
- 全部读入内存
- 读入索引到内存
我们这里选择第三种折中方案
使用
具体参数以及使用方法以官方文档为主
引包
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>${version}</version>
</dependency>
数据
引用完包之后,查询工具
的文件解决了,但是数据还是没有,如果手上没有合适的可以使用ip2Region
给的默认数据源
工具类
这里由于源码这里loadVectorIndexFromFile使用的是RandomAccessFile读入文件,而RandomAccessFile
是不能直接读入资源文件的,所以这里我们需要将资源文件转化为普通文件,然后再行读入,当然你也可以在服务器上存入数据源,但是这样来说和服务器就有耦合性了,不是特别大的数据源或者有专门的运维不建议这么做
@Service
@Slf4j
public class IpSearchUtils {
@Resource
private ResourceLoader resourceLoader;
private static String REGION_PATH;
private static byte[] REGION_INDEX;
@PostConstruct
public void init() {
try {
InputStream in = resourceLoader.getResource("classpath:xdb/ip2region.xdb").getInputStream();
File localIp2RegFile = FileUtil.writeFromStream(in, "localIp2Reg.xdb");
in.close();
REGION_PATH = localIp2RegFile.getPath();
REGION_INDEX = Searcher.loadVectorIndexFromFile(REGION_PATH);
} catch (Exception ex) {
log.error("[op:IpSearchUtils:init] ip package builder fail");
ex.printStackTrace();
}
}
public static String getIpName(String ip) {
Searcher searcher = null;
try {
searcher = Searcher.newWithVectorIndex(REGION_PATH, REGION_INDEX);
String region = searcher.search(ip);
searcher.close();
IpAddressObj ipAddressObj = new IpAddressObj(region);
if ("中国".equals(ipAddressObj.getCountry())) {
return ipAddressObj.getProvince() + ipAddressObj.getCity();
} else {
return ipAddressObj.getCountry();
}
} catch (Exception ex) {
log.error("[op:IpSearchUtils:init] ip [{}] convert fail", ip);
ex.printStackTrace();
if (null != searcher) {
try {
searcher.close();
} catch (IOException exception) {
log.error("[op:IpSearchUtils:init] ip package builder close fail");
exception.printStackTrace();
}
}
}
return null;
}
}
读入对象类
@Data
public class IpAddressObj {
private final static String SPLIT_SYMBOL = "\\|";
/**
* country
*/
private String country;
/**
* region
*/
private String region;
/**
* province
*/
private String province;
/**
* city
*/
private String city;
/**
* operator
*/
private String operator;
public IpAddressObj(String detail) {
if (!ObjectUtils.isEmpty(detail)) {
String[] values = detail.split(SPLIT_SYMBOL);
this.country = convertEmptyStr(values[0]);
this.region = convertEmptyStr(values[1]);
this.province = convertEmptyStr(values[2]);
this.city = convertEmptyStr(values[3]);
this.operator = convertEmptyStr(values[4]);
}
}
private String convertEmptyStr(String str) {
return "0".equals(str) ? "" : str;
}
}
其他说明
-
IP
获取这里在
nginx
配置中,获取实际地址(当然,如果对方使用代理那么确实无法定位真实地址),然后写入请求头中带到后端即可#... location / { proxy_pass http://localhost:9527; #保留原始访问信息 proxy_set_header Host $host; proxy_set_header your_set_forwarded_in_ngin $proxy_add_x_forwarded_for; proxy_set_header your_set_real_ip_key_in_nginx $remote_addr; proxy_set_header your_set_scheme_in_nginx $scheme; } #...
-
自动注入
这里我们使用继承进行自动注入,不使用
AOP
+注解
的方式,如果需要参数中带有地址直接继承IpAddressParam
即可@Data public class IpAddressParam { private final static String NGINX_SET_REAL_IP_KEY = "your_set_real_ip_key_in_nginx"; /** * ip地址 */ private String ipAddress; /** * ip位置 */ private String ipAddressName; public IpAddressParam() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String ip = request.getHeader(NGINX_SET_REAL_IP_KEY); if (!ObjectUtils.isEmpty(ip)) { this.ipAddress = ip; this.ipAddressName = IpSearchUtils.getIpName(ip); } } }