【改进】IP地址XDB
This commit is contained in:
@@ -49,7 +49,7 @@ dependencies {
|
|||||||
implementation 'cn.hutool:hutool-all:5.8.42' // hutool工具包
|
implementation 'cn.hutool:hutool-all:5.8.42' // hutool工具包
|
||||||
implementation 'io.springfox:springfox-swagger2:2.10.5'
|
implementation 'io.springfox:springfox-swagger2:2.10.5'
|
||||||
implementation 'com.github.xiaoymin:knife4j-openapi2-spring-boot-starter:4.4.0'
|
implementation 'com.github.xiaoymin:knife4j-openapi2-spring-boot-starter:4.4.0'
|
||||||
implementation 'org.lionsoul:ip2region:2.7.0'
|
implementation 'org.lionsoul:ip2region:3.3.4'
|
||||||
implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'
|
implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import com.corewing.app.common.Result;
|
|||||||
import com.corewing.app.dto.FeedbackRequest;
|
import com.corewing.app.dto.FeedbackRequest;
|
||||||
import com.corewing.app.entity.Feedback;
|
import com.corewing.app.entity.Feedback;
|
||||||
import com.corewing.app.service.FeedbackService;
|
import com.corewing.app.service.FeedbackService;
|
||||||
|
import com.corewing.app.util.CommonIpAddressUtil;
|
||||||
import com.corewing.app.util.DingTalkUtil;
|
import com.corewing.app.util.DingTalkUtil;
|
||||||
import com.corewing.app.util.I18nUtil;
|
import com.corewing.app.util.I18nUtil;
|
||||||
import com.corewing.app.util.Ip2RegionUtil;
|
|
||||||
import com.corewing.app.util.IpUtil;
|
import com.corewing.app.util.IpUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
@@ -30,12 +30,10 @@ public class AppFeedbackController {
|
|||||||
|
|
||||||
private final FeedbackService feedbackService;
|
private final FeedbackService feedbackService;
|
||||||
private final DingTalkUtil dingTalkUtil;
|
private final DingTalkUtil dingTalkUtil;
|
||||||
private final Ip2RegionUtil ip2RegionUtil;
|
|
||||||
|
|
||||||
public AppFeedbackController(FeedbackService feedbackService, DingTalkUtil dingTalkUtil, Ip2RegionUtil ip2RegionUtil) {
|
public AppFeedbackController(FeedbackService feedbackService, DingTalkUtil dingTalkUtil) {
|
||||||
this.feedbackService = feedbackService;
|
this.feedbackService = feedbackService;
|
||||||
this.dingTalkUtil = dingTalkUtil;
|
this.dingTalkUtil = dingTalkUtil;
|
||||||
this.ip2RegionUtil = ip2RegionUtil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,7 +62,7 @@ public class AppFeedbackController {
|
|||||||
if (success) {
|
if (success) {
|
||||||
// 获取提交IP和归属地
|
// 获取提交IP和归属地
|
||||||
String submitIp = IpUtil.getClientIp(httpRequest);
|
String submitIp = IpUtil.getClientIp(httpRequest);
|
||||||
String submitRegion = ip2RegionUtil.getRegion(submitIp);
|
String submitRegion = CommonIpAddressUtil.getCityInfo(submitIp);
|
||||||
|
|
||||||
// 推送到钉钉
|
// 推送到钉钉
|
||||||
sendFeedbackToDingTalk(feedback, submitIp, submitRegion);
|
sendFeedbackToDingTalk(feedback, submitIp, submitRegion);
|
||||||
|
|||||||
@@ -36,9 +36,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
|||||||
@Resource
|
@Resource
|
||||||
private VerifyCodeService verifyCodeService;
|
private VerifyCodeService verifyCodeService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private Ip2RegionUtil ip2RegionUtil;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private UserMapper userMapper;
|
private UserMapper userMapper;
|
||||||
|
|
||||||
@@ -219,7 +216,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
|||||||
// 设置注册IP和归属地
|
// 设置注册IP和归属地
|
||||||
if (StringUtils.hasText(registerIp)) {
|
if (StringUtils.hasText(registerIp)) {
|
||||||
user.setRegisterIp(registerIp);
|
user.setRegisterIp(registerIp);
|
||||||
user.setRegisterRegion(ip2RegionUtil.getRegion(registerIp));
|
user.setRegisterRegion(CommonIpAddressUtil.getCityInfo(registerIp));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存用户
|
// 保存用户
|
||||||
@@ -239,7 +236,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
|||||||
User user = new User();
|
User user = new User();
|
||||||
user.setId(userId);
|
user.setId(userId);
|
||||||
user.setLoginIp(loginIp);
|
user.setLoginIp(loginIp);
|
||||||
user.setLoginRegion(ip2RegionUtil.getRegion(loginIp));
|
user.setLoginRegion(CommonIpAddressUtil.getCityInfo(loginIp));
|
||||||
this.updateById(user);
|
this.updateById(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.lionsoul.ip2region.xdb.LongByteArray;
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
|
import org.lionsoul.ip2region.xdb.Version;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -24,7 +26,7 @@ public class CommonIpAddressUtil {
|
|||||||
|
|
||||||
private static final String LOCAL_REMOTE_HOST = "0:0:0:0:0:0:0:1";
|
private static final String LOCAL_REMOTE_HOST = "0:0:0:0:0:0:0:1";
|
||||||
|
|
||||||
private static final String IP2REGION_DB_PATH = "/ip2region.xdb";
|
private static final String IP2REGION_DB_PATH = "/ip2region_v4.xdb";
|
||||||
|
|
||||||
private static final Searcher searcher = initSearcher();
|
private static final Searcher searcher = initSearcher();
|
||||||
|
|
||||||
@@ -48,9 +50,9 @@ public class CommonIpAddressUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 1、从 dbPath 加载整个 xdb 到内存。
|
// 1、从 dbPath 加载整个 xdb 到内存。
|
||||||
final byte[] cBuff = Searcher.loadContentFromFile(dbFile.getPath());
|
final LongByteArray cBuff = Searcher.loadContentFromFile(dbFile.getPath());
|
||||||
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
||||||
return Searcher.newWithBuffer(cBuff);
|
return Searcher.newWithBuffer(Version.IPv4, cBuff);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(">>> CommonIpAddressUtil初始化异常:", e);
|
log.error(">>> CommonIpAddressUtil初始化异常:", e);
|
||||||
throw new RuntimeException("CommonIpAddressUtil初始化异常");
|
throw new RuntimeException("CommonIpAddressUtil初始化异常");
|
||||||
@@ -86,6 +88,7 @@ public class CommonIpAddressUtil {
|
|||||||
try {
|
try {
|
||||||
// 3、执行查询
|
// 3、执行查询
|
||||||
String region = searcher.search(ip.trim());
|
String region = searcher.search(ip.trim());
|
||||||
|
log.info(region);
|
||||||
return region.replace("0|", "").replace("|0", "");
|
return region.replace("0|", "").replace("|0", "");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return "未知";
|
return "未知";
|
||||||
|
|||||||
@@ -1,179 +0,0 @@
|
|||||||
package com.corewing.app.util;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IP 归属地查询工具类
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class Ip2RegionUtil {
|
|
||||||
|
|
||||||
private Searcher searcher;
|
|
||||||
private byte[] vIndex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 IP2Region 搜索器
|
|
||||||
*/
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
try {
|
|
||||||
// 从 classpath 加载 xdb 文件
|
|
||||||
ClassPathResource resource = new ClassPathResource("ipdb/ip2region.xdb");
|
|
||||||
InputStream inputStream = resource.getInputStream();
|
|
||||||
|
|
||||||
// 将 InputStream 转换为 byte[]
|
|
||||||
vIndex = new byte[inputStream.available()];
|
|
||||||
inputStream.read(vIndex);
|
|
||||||
inputStream.close();
|
|
||||||
|
|
||||||
// 创建 searcher 对象
|
|
||||||
searcher = Searcher.newWithBuffer(vIndex);
|
|
||||||
log.info("IP2Region 初始化成功");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("IP2Region 初始化失败: {}", e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据 IP 地址查询归属地
|
|
||||||
*
|
|
||||||
* @param ip IP 地址
|
|
||||||
* @return 归属地信息,格式:国家|区域|省份|城市|ISP
|
|
||||||
*/
|
|
||||||
public String getRegion(String ip) {
|
|
||||||
if (ip == null || ip.trim().isEmpty()) {
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过滤本地 IP
|
|
||||||
if (isLocalIp(ip)) {
|
|
||||||
return "局域网";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (searcher == null) {
|
|
||||||
log.warn("IP2Region 搜索器未初始化");
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
|
|
||||||
String region = searcher.search(ip);
|
|
||||||
return formatRegion(region);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("查询 IP 归属地失败, IP: {}, 异常: {}", ip, e.getMessage());
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化归属地信息
|
|
||||||
* 原始格式:国家|区域|省份|城市|ISP
|
|
||||||
* 格式化后:省份-城市
|
|
||||||
*
|
|
||||||
* @param region 原始归属地信息
|
|
||||||
* @return 格式化后的归属地
|
|
||||||
*/
|
|
||||||
private String formatRegion(String region) {
|
|
||||||
if (region == null || region.trim().isEmpty()) {
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String[] parts = region.split("\\|");
|
|
||||||
if (parts.length < 5) {
|
|
||||||
return region;
|
|
||||||
}
|
|
||||||
|
|
||||||
String country = parts[0];
|
|
||||||
String province = parts[2];
|
|
||||||
String city = parts[3];
|
|
||||||
|
|
||||||
// 处理国内 IP
|
|
||||||
if ("中国".equals(country)) {
|
|
||||||
// 如果省份和城市相同,只返回省份
|
|
||||||
if (province.equals(city)) {
|
|
||||||
return province;
|
|
||||||
}
|
|
||||||
// 如果省份或城市为 0,说明数据不完整
|
|
||||||
if ("0".equals(province) || "0".equals(city)) {
|
|
||||||
return "中国";
|
|
||||||
}
|
|
||||||
return province + "-" + city;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理国外 IP
|
|
||||||
if (!"0".equals(country) && !country.isEmpty()) {
|
|
||||||
return country;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "未知";
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("格式化归属地信息失败: {}", region, e);
|
|
||||||
return region;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否为本地 IP
|
|
||||||
*
|
|
||||||
* @param ip IP 地址
|
|
||||||
* @return 是否为本地 IP
|
|
||||||
*/
|
|
||||||
private boolean isLocalIp(String ip) {
|
|
||||||
return ip.startsWith("127.") ||
|
|
||||||
ip.startsWith("192.168.") ||
|
|
||||||
ip.startsWith("10.") ||
|
|
||||||
ip.startsWith("172.16.") ||
|
|
||||||
ip.startsWith("172.17.") ||
|
|
||||||
ip.startsWith("172.18.") ||
|
|
||||||
ip.startsWith("172.19.") ||
|
|
||||||
ip.startsWith("172.20.") ||
|
|
||||||
ip.startsWith("172.21.") ||
|
|
||||||
ip.startsWith("172.22.") ||
|
|
||||||
ip.startsWith("172.23.") ||
|
|
||||||
ip.startsWith("172.24.") ||
|
|
||||||
ip.startsWith("172.25.") ||
|
|
||||||
ip.startsWith("172.26.") ||
|
|
||||||
ip.startsWith("172.27.") ||
|
|
||||||
ip.startsWith("172.28.") ||
|
|
||||||
ip.startsWith("172.29.") ||
|
|
||||||
ip.startsWith("172.30.") ||
|
|
||||||
ip.startsWith("172.31.") ||
|
|
||||||
"localhost".equalsIgnoreCase(ip) ||
|
|
||||||
"0:0:0:0:0:0:0:1".equals(ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取完整的归属地信息(用于调试)
|
|
||||||
*
|
|
||||||
* @param ip IP 地址
|
|
||||||
* @return 完整的归属地信息
|
|
||||||
*/
|
|
||||||
public String getFullRegion(String ip) {
|
|
||||||
if (ip == null || ip.trim().isEmpty()) {
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLocalIp(ip)) {
|
|
||||||
return "局域网";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (searcher == null) {
|
|
||||||
log.warn("IP2Region 搜索器未初始化");
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
|
|
||||||
return searcher.search(ip);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("查询 IP 归属地失败, IP: {}, 异常: {}", ip, e.getMessage());
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Reference in New Issue
Block a user