diff --git a/pom.xml b/pom.xml
index f420f3cd4..a121c323a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,7 +39,7 @@
1.16.7
2.7.0
-
+ 4.2.1
2.28.22
0.31.3
@@ -320,6 +320,12 @@
${ip2region.version}
+
+ com.maxmind.geoip2
+ geoip2
+ ${geoip2.version}
+
+
commons-io
commons-io
diff --git a/ruoyi-admin/src/main/resources/GeoLite2-City.mmdb b/ruoyi-admin/src/main/resources/GeoLite2-City.mmdb
new file mode 100644
index 000000000..0ec737a88
Binary files /dev/null and b/ruoyi-admin/src/main/resources/GeoLite2-City.mmdb differ
diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml
index ad37e90db..74c88d8ab 100644
--- a/ruoyi-common/ruoyi-common-core/pom.xml
+++ b/ruoyi-common/ruoyi-common-core/pom.xml
@@ -94,6 +94,12 @@
ip2region
+
+
+ com.maxmind.geoip2
+ geoip2
+
+
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java
index 3f7cd57a9..dac4b598c 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java
@@ -1,11 +1,13 @@
package org.dromara.common.core.utils.ip;
-import cn.hutool.core.net.NetUtil;
import cn.hutool.http.HtmlUtil;
-import org.dromara.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.StringUtils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
/**
* 获取地址类
@@ -17,17 +19,19 @@ import lombok.extern.slf4j.Slf4j;
public class AddressUtils {
// 未知地址
- public static final String UNKNOWN = "XX XX";
+ public static final String UNKNOWN = "未知";
public static String getRealAddressByIP(String ip) {
if (StringUtils.isBlank(ip)) {
return UNKNOWN;
}
- // 内网不查询
- ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
- if (NetUtil.isInnerIP(ip)) {
- return "内网IP";
+ ip = HtmlUtil.cleanHtmlTag(ip).trim();
+ try {
+ InetAddress ipAddress = InetAddress.getByName(ip);
+ return RegionUtils.getCityInfo(ipAddress);
+ } catch (UnknownHostException e) {
+ log.error("IP地址离线获取城市异常 {}", ip);
+ return UNKNOWN;
}
- return RegionUtils.getCityInfo(ip);
}
}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
index 6e2a44e00..48772907f 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
@@ -3,16 +3,26 @@ package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.ObjectUtil;
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.record.City;
+import com.maxmind.geoip2.record.Country;
+import com.maxmind.geoip2.record.Subdivision;
+import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.file.FileUtils;
-import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.File;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.Locale;
/**
* 根据ip地址定位工具类,离线方式
- * 参考地址:集成 ip2region 实现离线IP地址定位库
*
* @author lishuyan
*/
@@ -20,19 +30,22 @@ import java.io.File;
public class RegionUtils {
private static final Searcher SEARCHER;
+ private static final DatabaseReader reader;
+ private static final String locale;
static {
- String fileName = "/ip2region.xdb";
- File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
- if (!FileUtils.exist(existFile)) {
- ClassPathResource fileStream = new ClassPathResource(fileName);
+ //ipv4初始化
+ String fileName4 = "/ip2region.xdb";
+ File existFile4 = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName4);
+ if (!FileUtils.exist(existFile4)) {
+ ClassPathResource fileStream = new ClassPathResource(fileName4);
if (ObjectUtil.isEmpty(fileStream.getStream())) {
- throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
+ throw new ServiceException("RegionUtils初始化失败,原因:IPv4地址库数据不存在!");
}
- FileUtils.writeFromStream(fileStream.getStream(), existFile);
+ FileUtils.writeFromStream(fileStream.getStream(), existFile4);
}
- String dbPath = existFile.getPath();
+ String dbPath = existFile4.getPath();
// 1、从 dbPath 加载整个 xdb 到内存。
byte[] cBuff;
@@ -47,21 +60,69 @@ public class RegionUtils {
} catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
}
+
+ //ipv6初始化
+ //文件来源 https://download.db-ip.com/free/dbip-city-lite-2025-03.mmdb.gz (db-ip官方免费版本,中文支持不好)
+ //文件来源 https://gitee.com/allen0769/GeoLite.mmdb.git
+ String fileName6 = "/GeoLite2-City.mmdb";
+ File existFile6 = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName6);
+ if (!FileUtils.exist(existFile6)) {
+ ClassPathResource fileStream = new ClassPathResource(fileName6);
+ if (ObjectUtil.isEmpty(fileStream.getStream())) {
+ throw new ServiceException("RegionUtils初始化失败,原因:IP6地址库数据不存在!");
+ }
+ FileUtils.writeFromStream(fileStream.getStream(), existFile6);
+ }
+ try {
+ reader = new DatabaseReader.Builder(existFile6).build();
+ } catch (IOException e) {
+ throw new ServiceException("RegionUtils初始化失败,原因:从GeoLite2-City.mmdb文件加载内容失败!" + e.getMessage());
+ }
+ locale = Locale.getDefault().toString().replace("_", "-");
}
/**
* 根据IP地址离线获取城市
*/
- public static String getCityInfo(String ip) {
+ public static String getCityInfo(InetAddress ipAddress) {
try {
- ip = ip.trim();
- // 3、执行查询
- String region = SEARCHER.search(ip);
- return region.replace("0|", "").replace("|0", "");
+ if (ipAddress.isSiteLocalAddress() || ipAddress.isLinkLocalAddress() || ipAddress.isLoopbackAddress()) {
+ return "内网IP";
+ }
+ if (ipAddress instanceof Inet4Address) {
+ return getCityInfoIpv4(ipAddress);
+ } else if (ipAddress instanceof Inet6Address) {
+ return getCityInfoIpv6(ipAddress);
+ } else {
+ return "未知";
+ }
} catch (Exception e) {
- log.error("IP地址离线获取城市异常 {}", ip);
+ log.error("IP地址 {} 离线获取城市异常 {}", ipAddress.getAddress(), e.getMessage());
return "未知";
}
}
+ /**
+ * 根据IPV4地址离线获取城市
+ */
+ private static String getCityInfoIpv4(InetAddress ipAddress) throws Exception {
+ String region = SEARCHER.search(ipAddress.getHostAddress());
+ //中国|江苏省|南京市|电信
+ return region.replace("0|", "").replace("|0", "");
+ }
+
+ /**
+ * 根据IPV6地址离线获取城市
+ */
+ private static String getCityInfoIpv6(InetAddress ipAddress) throws IOException, GeoIp2Exception {
+ CityResponse cityResponse = reader.city(ipAddress);
+ Country country = cityResponse.getCountry();
+ Subdivision subdivision = cityResponse.getMostSpecificSubdivision();
+ City city = cityResponse.getCity();
+ String countryStr = country.getNames().get(locale) != null ? country.getNames().get(locale) : country.getName();
+ String subdivisionStr = subdivision.getNames().get(locale) != null ? subdivision.getNames().get(locale) : subdivision.getName();
+ String cityStr = city.getNames().get(locale) != null ? city.getNames().get(locale) : city.getName();
+ return countryStr + "|" + subdivisionStr + "|" + cityStr;
+ }
+
}
diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf
index 22b074f1d..1eb2e6cc5 100644
--- a/script/docker/nginx/conf/nginx.conf
+++ b/script/docker/nginx/conf/nginx.conf
@@ -39,10 +39,12 @@ http {
server {
listen 80;
+ listen [::]:80 ipv6only=on;
server_name localhost;
# https配置参考 start
#listen 443 ssl;
+ #listen [::]:443 ssl ipv6only=on;
# 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径
#ssl on;