?細心的小伙伴可能會發(fā)現(xiàn)化戳,抖音新上線了IP屬地的功能,小伙伴在發(fā)表動態(tài)埋凯、發(fā)表評論以及聊天的時候点楼,都會顯示自己的IP屬地信息
下面扫尖,我就來講講,Java中是如何獲取IP屬地的掠廓,主要分為以下幾步
通過 HttpServletRequest 對象藏斩,獲取用戶的IP地址
通過 IP 地址,獲取對應的省份却盘、城市
首先需要寫一個IP獲取的工具類狰域,因為每一次用戶的Request請求,都會攜帶上請求的IP地址放到請求頭中黄橘。
public class IpUtil {
? ? public static String getIpAddr(ServerHttpRequest request) {
? ? ? ? HttpHeaders headers = request.getHeaders();
? ? ? ? String ipAddress = headers.getFirst("X-Forwarded-For");
? ? ? ? if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
? ? ? ? ? ? ipAddress = headers.getFirst("Proxy-Client-IP");
? ? ? ? }
? ? ? ? if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
? ? ? ? ? ? ipAddress = headers.getFirst("WL-Proxy-Client-IP");
? ? ? ? }
? ? ? ? if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
? ? ? ? ? ? ipAddress = request.getRemoteAddress().getAddress().getHostAddress();
? ? ? ? ? ? if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
? ? ? ? ? ? ? ? // 根據(jù)網(wǎng)卡取本機配置的IP
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? InetAddress inet = InetAddress.getLocalHost();
? ? ? ? ? ? ? ? ? ? ipAddress = inet.getHostAddress();
? ? ? ? ? ? ? ? } catch (UnknownHostException e) {
? ? ? ? ? ? ? ? ? ? log.error("根據(jù)網(wǎng)卡獲取本機配置的IP異常", e);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 對于通過多個代理的情況兆览,第一個IP為客戶端真實IP,多個IP按照','分割
? ? ? ? if (ipAddress != null && ipAddress.indexOf(",") > 0) {
? ? ? ? ? ? ipAddress = ipAddress.split(",")[0];
? ? ? ? }
? ? ? ? return ipAddress;
? ? }
}
這里有三個名詞塞关,分別是
X-Forwarded-For:一個 HTTP擴展頭部抬探,主要是為了讓Web服務器獲取訪問用戶的真實IP地址。每個IP地址帆赢,每個值通過逗號+空格分開小压,最左邊是最原始客戶端的IP地址,中間如果有多層代理椰于,每?層代理會將連接它的客戶端IP追加在X-Forwarded-For右邊怠益。
X-Real-IP:一般只記錄真實發(fā)出請求的客戶端IP
Proxy-Client-IP:這個一般是經(jīng)過Apache http服務器的請求才會有,用Apache http做代理時一般會加上Proxy-Client-IP請求頭
WL-Proxy-Client-IP:也是通過 Apache http 服務器瘾婿,在weblogic插件加上的頭蜻牢。
在我們獲取到用戶的IP地址后,那么就可以獲取對應的ip信息了
我在Github沖浪的時候偏陪,發(fā)現(xiàn)了Ip2region項目抢呆。
一個準確率99.9%的離線IP地址定位庫,0.0x毫秒級查詢笛谦,ip2region.db數(shù)據(jù)庫只有數(shù)MB抱虐,提供了 java,php,c,python,nodejs,golang,c# 等查詢綁定和Binary,B樹饥脑,內存三種查詢算法恳邀。
數(shù)據(jù)聚合了一些知名ip到地名查詢提供商的數(shù)據(jù),這些是他們官方的的準確率好啰,經(jīng)測試著實比經(jīng)典的純真IP定位準確一些轩娶。ip2region的數(shù)據(jù)聚合自以下服務商的開放API或者數(shù)據(jù)儿奶。
80%, 淘寶IP地址庫, http://ip.taobao.com/
≈10%, GeoIP, https://geoip.com/
≈2%, 純真IP庫, http://www.cz88.net/
備注:如果上述開放API或者數(shù)據(jù)都不給開放數(shù)據(jù)時ip2region將停止數(shù)據(jù)的更新服務框往。
每條ip數(shù)據(jù)段都固定了格式:
_城市Id|國家|區(qū)域|省份|城市|ISP_
只有中國的數(shù)據(jù)精確到了城市,其他國家有部分數(shù)據(jù)只能定位到國家闯捎,后前的選項全部是0椰弊,已經(jīng)包含了全部你能查到的大大小小的國家
生成的數(shù)據(jù)庫文件ip2region.db只有幾MB许溅,最小的版本只有1.5MB,隨著數(shù)據(jù)的詳細度增加數(shù)據(jù)庫的大小也慢慢增大秉版,目前還沒超過8MB贤重。
內置的三種查詢算法
全部的查詢客戶端單次查詢都在0.x毫秒級別,內置了三種查詢算法
memory算法:整個數(shù)據(jù)庫全部載入內存清焕,單次查詢都在0.1x毫秒內并蝗,C語言的客戶端單次查詢在0.00x毫秒級別。
binary算法:基于二分查找秸妥,基于ip2region.db文件滚停,不需要載入內存,單次查詢在0.x毫秒級別粥惧。
b-tree算法:基于btree算法键畴,基于ip2region.db文件,不需要載入內存突雪,單詞查詢在0.x毫秒級別起惕,比binary算法更快。
ip2region安裝
下面咏删,就讓我們給項目引入ip2region惹想,進行ip信息轉換吧
首先引入maven依賴
<dependency>
????<groupId>org.lionsoul</groupId>
????<artifactId>ip2region</artifactId>
????<version>1.7.2</version>
</dependency>
然后編寫一個工具類IpUtils,首先需要加載ip2region.db文件
static?{
????dbPath?=?createFtlFileByFtlArray()?+?"ip2region.db";
????try?{
????????config?=?new?DbConfig();
????}?catch?(DbMakerConfigException?e)?{
????????e.printStackTrace();
????}
????try?{
????????searcher?=?new?DbSearcher(config,?dbPath);
????}?catch?(FileNotFoundException?e)?{
????????e.printStackTrace();
????}
}
在加載的時候督函,需要下載倉庫中的ip2region.db文件勺馆,然后放到resource目錄下
然后,通過內置的三種算法侨核,分別轉換用戶ip地址
????public?static?String?getCityInfo(String?ip)?{
????????if?(StringUtils.isEmpty(dbPath))?{
????????????log.error("Error:?Invalid?ip2region.db?file");
????????????return?null;
????????}
????????if(config?==?null?||?searcher?==?null){
????????????log.error("Error:?DbSearcher?or?DbConfig?is?null");
????????????return?null;
????????}
????????//查詢算法
????????//B-tree,?B樹搜索(更快)
????????int?algorithm?=?DbSearcher.BTREE_ALGORITHM;
????????//Binary,使用二分搜索
????????//DbSearcher.BINARY_ALGORITHM
????????//Memory,加載內存(最快)
????????//DbSearcher.MEMORY_ALGORITYM
????????try?{
????????????//?使用靜態(tài)代碼塊草穆,減少文件讀取操作
//????????????DbConfig?config?=?new?DbConfig();
//????????????DbSearcher?searcher?=?new?DbSearcher(config,?dbPath);
????????????//define?the?method
????????????Method?method?=?null;
????????????switch?(algorithm)?{
????????????????case?DbSearcher.BTREE_ALGORITHM:
????????????????????method?=?searcher.getClass().getMethod("btreeSearch",?String.class);
????????????????????break;
????????????????case?DbSearcher.BINARY_ALGORITHM:
????????????????????method?=?searcher.getClass().getMethod("binarySearch",?String.class);
????????????????????break;
????????????????case?DbSearcher.MEMORY_ALGORITYM:
????????????????????method?=?searcher.getClass().getMethod("memorySearch",?String.class);
????????????????????break;
????????????????default:
????????????}
????????????DataBlock?dataBlock?=?null;
????????????if?(Util.isIpAddress(ip)?==?false)?{
????????????????System.out.println("Error:?Invalid?ip?address");
????????????}
????????????dataBlock?=?(DataBlock)?method.invoke(searcher,?ip);
????????????String?ipInfo?=?dataBlock.getRegion();
????????????if?(!StringUtils.isEmpty(ipInfo))?{
????????????????ipInfo?=?ipInfo.replace("|0",?"");
????????????????ipInfo?=?ipInfo.replace("0|",?"");
????????????}
????????????return?ipInfo;
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????????return?null;
????}
下面,我們編寫main函數(shù)進行測試搓译,發(fā)現(xiàn)可以正常的解析出ip信息
由于 ip 屬地在國內的話悲柱,只會展示省份,而國外的話些己,只會展示國家豌鸡。所以我們還需要對這個方法進行一下封裝,得到獲取 IP 屬地的信息段标。
/**
?*?獲取IP屬地
?*?@param?ip
?*?@return
?*/
public?static?String?getIpPossession(String?ip)?{
????String?cityInfo?=?getCityInfo(ip);
????if?(!StringUtils.isEmpty(cityInfo))?{
????????cityInfo?=?cityInfo.replace("|",?"?");
????????String[]?cityList?=?cityInfo.split("?");
????????if?(cityList.length?>?0)?{
????????????//?國內的顯示到具體的省
????????????if?("中國".equals(cityList[0]))?{
????????????????if?(cityList.length?>?1)?{
????????????????????return?cityList[1];
????????????????}
????????????}
????????????//?國外顯示到國家
????????????return?cityList[0];
????????}
????}
????return?"未知";
}
下面涯冠,我們在找一個 國外的IP測試一下效果”婆樱可以看到已經(jīng)能夠正常的顯示IP屬地信息了~
到這里如果獲取用戶的 IP 屬地已經(jīng)完成啦蛇更,如果想要了解關于更多ip2region的功能,歡迎訪問其Github地址進行學習。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?項目地址
https://github.com/lionsoul2014/ip2region
碼文不易派任,學到的小伙伴點一點關注哦砸逊,一鍵三連加收藏哦!U乒洹师逸!
有不懂的小伙伴可以加下我的微信,我們私底下悄悄內卷~~~