Java實(shí)現(xiàn)通過IP計(jì)算分析歸屬地實(shí)例分享
在產(chǎn)品中可能存在不同客戶端,請(qǐng)求同一個(gè)服務(wù)端接口的場(chǎng)景。
例如小程序和App或者瀏覽器中,如果需要對(duì)請(qǐng)求的歸屬地進(jìn)行分析,前提是需要先獲取請(qǐng)求所在的國(guó)家或城市,這種定位通常需要主動(dòng)授權(quán),而用戶一般是不愿意提供的,就需要通過請(qǐng)求的IP來進(jìn)行歸屬地計(jì)算。
IP地址一般分為兩種,IPV4和IPV6,相應(yīng)的計(jì)算方式也有差異,以國(guó)家維度來參考,每個(gè)國(guó)家都有對(duì)應(yīng)的網(wǎng)段范圍,計(jì)算網(wǎng)段中的最小和最大IP地址的對(duì)應(yīng)數(shù)值,然后對(duì)比請(qǐng)求的IP地址,來判斷屬于哪個(gè)國(guó)家的網(wǎng)段范圍。
import cn.hutool.core.net.Ipv4Util; import cn.hutool.core.util.StrUtil; import java.math.BigInteger; import java.net.InetAddress; public class IpCalculate { public static void main(String[] args) throws Exception { // IPv4 網(wǎng)段 String ipv4Network = "IPv4 網(wǎng)段"; String[] ipv4Param = StrUtil.splitToArray(ipv4Network, "/"); // IPv4 起始和結(jié)束IP String ipv4StartIp = Ipv4Util.getBeginIpStr(ipv4Param[0],Integer.parseInt(ipv4Param[1])); String ipv4OverIp = Ipv4Util.getEndIpStr(ipv4Param[0],Integer.parseInt(ipv4Param[1])); System.out.println(ipv4StartIp); System.out.println(ipv4OverIp); // IPv4 起始和結(jié)束IP對(duì)應(yīng)的Long值 System.out.println(Ipv4Util.ipv4ToLong(ipv4StartIp)); System.out.println(Ipv4Util.ipv4ToLong(ipv4OverIp)); // IPv6 網(wǎng)段 String ipv6Network = "IPv6 網(wǎng)段"; String[] ipv6Param =ipv6Network.split("/"); int prefixLength = Integer.parseInt(ipv6Param[1]); // IPv6 起始和結(jié)束IP InetAddress baseAddress = InetAddress.getByName(ipv6Param[0]); BigInteger baseValue = new BigInteger(1, baseAddress.getAddress()); BigInteger mask = BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE) .shiftRight(128 - prefixLength).shiftLeft(128 - prefixLength); BigInteger minIp = baseValue.and(mask); BigInteger maxIp = minIp.add(BigInteger.ONE.shiftLeft(128 - prefixLength).subtract(BigInteger.ONE)); System.out.println(toIPv6String(minIp)); System.out.println(toIPv6String(maxIp)); // IPv6 起始和結(jié)束IP對(duì)應(yīng)的Long值 System.out.println(minIp); System.out.println(maxIp); } private static String toIPv6String(BigInteger value) throws Exception { byte[] bytes = value.toByteArray(); byte[] ipv6Bytes = new byte[16]; int start = bytes.length > 16 ? bytes.length - 16 : 0; int length = Math.min(bytes.length, 16); System.arraycopy(bytes, start, ipv6Bytes, 16 - length, length); return InetAddress.getByAddress(ipv6Bytes).getHostAddress(); } }
不過網(wǎng)段地址和國(guó)家的對(duì)應(yīng)關(guān)系需要進(jìn)行維護(hù),如果歸屬地分析不需要非常精準(zhǔn),可以直接使用開源的字典庫,比如使用比較多的就是GeoIP2組件。
<dependency> <groupId>com.maxmind.geoip2groupId> <artifactId>geoip2</artifactId> </dependency>
通過組件中提供的API加載相應(yīng)的文件字典,然后傳入IP地址進(jìn)行歸屬地判斷,這里要注意爭(zhēng)議和敏感地區(qū)的處理,如果出錯(cuò)產(chǎn)品可不止是上熱搜的問題了。
import com.maxmind.geoip2.DatabaseReader; import java.io.File; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; public class GeoIpTool { public static void main(String[] args) throws Exception { // 讀取IP庫文件 File ipFile = new File("IP文件庫"); DatabaseReader reader = new DatabaseReader.Builder(ipFile).build(); // IPV4地址 InetAddress ipV4 = InetAddress.getByName("IPV4"); if (ipV4 instanceof Inet4Address){ System.out.println(reader.country(ipV4)); System.out.println(reader.country(ipV4).getCountry()); // 默認(rèn)英文名 System.out.println(reader.country(ipV4).getCountry().getName()); // 查詢中文名 System.out.println(reader.country(ipV4).getCountry().getNames().get("zh-CN")); } // IPV6地址 InetAddress ipV6 = InetAddress.getByName("IPV6"); if (ipV6 instanceof Inet6Address){ System.out.println(reader.country(ipV6)); System.out.println(reader.country(ipV6).getCountry()); // 默認(rèn)英文名 System.out.println(reader.country(ipV6).getCountry().getName()); // 查詢中文名 System.out.println(reader.country(ipV6).getCountry().getNames().get("zh-CN")); } } }
如果需要非常精確的實(shí)時(shí)歸屬地分析,可以購買專業(yè)的IP網(wǎng)段數(shù)據(jù),實(shí)時(shí)更新到本地的數(shù)據(jù)庫中,作為IP字典使用,獲取請(qǐng)求的IP后,直接范圍匹配即可。
CREATE TABLE `ip_place` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `network` varchar(100) DEFAULT NULL COMMENT '網(wǎng)段區(qū)間', `min_ip` bigint(20) DEFAULT NULL COMMENT '最小IP', `max_ip` bigint(20) DEFAULT NULL COMMENT '最大IP', `min_ip_number` bigint(20) DEFAULT NULL COMMENT '最小IP數(shù)值', `max_ip_number` bigint(20) DEFAULT NULL COMMENT '最大IP數(shù)值', `ip_place` varchar(100) DEFAULT NULL COMMENT '歸屬地', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP歸屬地';
最后需要補(bǔ)充說一句,對(duì)于很多標(biāo)準(zhǔn)的數(shù)據(jù),盡可能在項(xiàng)目最初就設(shè)計(jì)好字典枚舉或者數(shù)據(jù)表,避免后續(xù)規(guī)范時(shí)面臨數(shù)據(jù)清洗的問題。
到此這篇關(guān)于Java實(shí)現(xiàn)通過IP計(jì)算分析歸屬地實(shí)例分享的文章就介紹到這了,更多相關(guān)通過IP計(jì)算分析歸屬地內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中@ConditionalOnProperty的使用及作用詳解
這篇文章主要介紹了SpringBoot中@ConditionalOnProperty的使用及作用詳解,@ConditionalOnProperty通過讀取本地配置文件中的值來判斷 某些 Bean 或者 配置類 是否加入spring 中,需要的朋友可以參考下2024-01-01自己動(dòng)手在Spring-Boot上加強(qiáng)國(guó)際化功能的示例
這篇文章主要介紹了自己動(dòng)手在Spring-Boot上加強(qiáng)國(guó)際化功能的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04java實(shí)現(xiàn)gif動(dòng)畫效果(java顯示動(dòng)態(tài)圖片)
這篇文章主要介紹了java實(shí)現(xiàn)gif動(dòng)畫效果示例(java顯示動(dòng)態(tài)圖片),需要的朋友可以參考下2014-04-04Java開發(fā)中POJO和JSON互轉(zhuǎn)時(shí)如何忽略隱藏字段的問題
這篇文章主要介紹了Java開發(fā)中POJO和JSON互轉(zhuǎn)時(shí)如何忽略隱藏字段的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02Springboot mybatisplus如何解決分頁組件IPage失效問題
這篇文章主要介紹了Springboot mybatisplus如何解決分頁組件IPage失效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Java統(tǒng)計(jì)一個(gè)字符串在另外一個(gè)字符串出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java統(tǒng)計(jì)一個(gè)字符串在另外一個(gè)字符串出現(xiàn)次數(shù)的方法,涉及java字符串遍歷、正則匹配等相關(guān)操作技巧,需要的朋友可以參考下2018-03-03基于javascript實(shí)現(xiàn)獲取最短路徑算法代碼實(shí)例
這篇文章主要介紹了基于javascript實(shí)現(xiàn)獲取最短路徑算法代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02Java?EasyExcel實(shí)現(xiàn)合并相同內(nèi)容單元格與動(dòng)態(tài)標(biāo)題功能
這篇文章主要為大家詳細(xì)介紹了Java?EasyExcel如何實(shí)現(xiàn)合并相同內(nèi)容單元格與動(dòng)態(tài)標(biāo)題功能,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考下2023-12-12