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;