ip2region
ip2region 是什么?
狮子的魂/ip2region - 码云 - 开源中国 (gitee.com)
官方:ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nodejs,golang,c#等查询绑定和Binary,B树,内存三种查询算法
其实就是一个库,用于 IP地址解析定位用的,支持很多客户端,速度很快也不占内存,目前github已经到了10k star 了
特性:
速度快,内置三种查询算法,支持毫秒级查询,全部的查询客户端单次查询都在 0.x 毫秒级别,内置了三种查询算法
- memory 算法:整个数据库全部载入内存,单次查询都在0.1x毫秒内,C语言的客户端单次查询在0.00x毫秒级别。
- binary 算法:基于二分查找,基于ip2region.db文件,不需要载入内存,单次查询在0.x毫秒级别。
- b-tree 算法:基于btree算法,基于ip2region.db文件,不需要载入内存,单词查询在0.x毫秒级别,比binary算法更快。
准确率高,数据聚合多个供应商的数据 体积小,空间优化形成了仅仅几兆的 ip2region.db 文件 支持多客户端,支持很多客户端,java、C#、php、c、python、nodejs、php扩展(php5和php7)、golang、rust、lua、lua_c, nginx 它的数据聚合了一些知名ip到地名查询提供商的数据,例如:(如果一下开放API或者数据都不给开放数据时ip2region将停止数据的更新服务)
淘宝IP地址库, http://ip.taobao.com/ GeoIP, https://geoip.com/ 纯真IP库, http://www.cz88.net/
使用
我们这里不导入官方的依赖,我们导入mica-ip2region 依赖,mica-ip2region 是 ip2region 的封装,方便 spring boot 用户使用。
mica: Spring Cloud 微服务开发核心工具集。 - Gitee.com
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>mica-ip2region</artifactId>
<version>2.6.8</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.4</version>
</dependency>
</dependencies>
编写一个浏览器工具类
这里有三个名词,分别是
- X-Forwarded-For:一个 HTTP 扩展头部,主要是为了让 Web 服务器获取访问用户的真实 IP 地址。每个 IP 地址,每个值通过逗号+空格分开,最左边是最原始客户端的 IP 地址,中间如果有多层代理,每?层代理会将连接它的客户端 IP 追加在 X-Forwarded-For 右边。
- X-Real-IP:一般只记录真实发出请求的客户端IP
- Proxy-Client-IP:这个一般是经过 Apache http 服务器的请求才会有,用 Apache http 做代理时一般会加上 Proxy-Client-IP 请求头
- WL-Proxy-Client-IP:也是通过 Apache http 服务器,在 weblogic 插件加上的头。
在我们获取到用户的 IP 地址后,那么就可以获取对应的 ip 信息了
@Component
public class BrowserUtil {
private static final String UNKNOWN = "unknown";
public String getIp(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();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
}
return ip;
}
public JSONObject getBrowser(HttpServletRequest request) {
String browser = request.getHeader("User-Agent");
UserAgent ua = UserAgentUtil.parse(browser);
String browserType = ua.getBrowser().toString();
String version = ua.getVersion();
String engine = ua.getEngine().toString();
String engineVersion = ua.getEngineVersion();
String os = ua.getOs().toString();
String platform = ua.getPlatform().toString();
Integer mobile = ua.isMobile() ? 1 : 0;
JSONObject json = new JSONObject();
json.set("browserType", browserType).set("platform", platform).set("mobile", mobile).set("os", os).set("platform", platform).set("version", version)
.set("engine",engine).set("engineVersion", engineVersion);
return json;
}
}
编写控制类
@RestController
@RequestMapping("/browser")
public class IpController {
@Autowired
BrowserUtil browserUtil;
@Autowired
private Ip2regionSearcher ip2regionSearcher;
@GetMapping("/getAddress")
public ResponseEntity<Object> address(HttpServletRequest request) throws IOException {
String ip = browserUtil.getIp(request);
IpInfo ipInfo = ip2regionSearcher.memorySearch(ip);
JSONObject entries = new JSONObject();
if (ipInfo != null) {
String country = ipInfo.getCountry();
String province = ipInfo.getProvince();
String city = ipInfo.getCity();
String isp = ipInfo.getIsp();
entries.set("country", country);
entries.set("province", province);
entries.set("city", city);
entries.set("isp", isp);
entries.set("ip", ip);
}
return new ResponseEntity<>(entries, HttpStatus.OK);
}
@GetMapping("/getAddressByIp")
public ResponseEntity<Object> addressByIp(@RequestParam("ip") String ip) throws IOException {
IpInfo ipInfo = ip2regionSearcher.binarySearch(ip);
assert ipInfo != null;
JSONObject entries = new JSONObject();
if (ipInfo != null) {
String country = ipInfo.getCountry();
String province = ipInfo.getProvince();
String city = ipInfo.getCity();
String isp = ipInfo.getIsp();
entries.set("country", country);
entries.set("province", province);
entries.set("city", city);
entries.set("isp", isp);
entries.set("ip", ip);
}
return new ResponseEntity<>(entries, HttpStatus.OK);
}
@GetMapping("/getBrowserInfo")
public JSONObject getBrowser(HttpServletRequest request) throws IOException {
return browserUtil.getBrowser(request);
}
@GetMapping("/cz88")
public JSONObject cs88(@RequestParam String ip) {
String fullUrl="https://www.cz88.net/api/cz88/ip/base?ip="+ip;
String result= HttpUtil.createGet(fullUrl).execute().body();
return JSONUtil.parseObj(result);
}
}
测试
Docker部署
idea使用Dockerfile创建镜像
文件内容
FROM openjdk:latest
EXPOSE 9956
ADD target/ipBelongingPlace-0.0.1-SNAPSHOT.jar app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["nohup","java","-jar","/app.jar","&"]
构建镜像
点击Run运行即可,这样就创建完成了镜像
运行镜像
docker run -d -p 9956:9956 --name bootIp springboot-ip:1.0
日志:
测试:
windowsDocker
服务器Docker
方法接口文档
GET 根据发送请求直接查询ip
GET /browser/getAddress
{
"city":"内网IP",
"isp":"内网IP",
"ip":"172.17.0.1"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|
200 | OK | 成功 | Inline |
GET 根据发送请求查询浏览器信息
GET /browser/getBrowserInfo
如果使用apifox等测试工具是没有信息的
{
"browserType": "Unknown",
"platform": "Unknown",
"mobile": 0,
"os": "Unknown",
"engine": "Unknown"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|
200 | OK | 成功 | Inline |
GET 使用ip2region依赖查询ip
GET /browser/getAddressByIp
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
---|
ip | query | string | 否 | none |
{
"country":"中国",
"province":"上海",
"city":"上海",
"isp":"联通",
"ip":"220.248.12.158"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|
200 | OK | 成功 | Inline |
GET 使用纯真ip库查询ip
GET /browser/cz88
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
---|
ip | query | string | 否 | none |
{
"code": 200,
"success": true,
"message": "操作成功",
"data": {
"ip": "103.216.43.5",
"countryCode": "CN",
"country": "中国",
"province": "北京",
"city": "北京",
"districts": "朝阳区",
"isp": "皓宽网络",
"geocode": "156011001005",
"netWorkType": "专线用户",
"mbRate": "暂未发现",
"score": "58%",
"honeypot": "暂未发现",
"netAddress": "登录查看",
"actionAddress": [
"中国-北京-北京-朝阳区"
],
"company": "暂未发现",
"locations": [
{
"latitude": "39.92**",
"longitude": "116.44**",
"radius": 34132
}
],
"openPorts": [],
"domains": [],
"breadRateMap": {
"RuijieNe": "0.191",
"其他": "0.500",
"Tp-LinkT": "0.309"
},
"deviceRateMap": {
"安卓": "0.677",
"iOS": "0.323"
}
},
"time": "2022-07-08 16:53:49"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|
200 | OK | 成功 | Inline |
博客详情 (warmwood.xyz)
|