Java根據(jù)ip地址獲取歸屬地實例詳解
引言
最近,各大平臺都新增了評論區(qū)顯示發(fā)言者ip歸屬地的功能,例如嗶哩嗶哩,微博,知乎等等。


Java 中是如何獲取 IP 屬地的
主要分為以下幾步
- 通過 HttpServletRequest 對象,獲取用戶的 IP 地址
- 通過 IP 地址,獲取對應(yīng)的省份、城市
首先需要寫一個 IP 獲取的工具類
因為每一次用戶的 Request 請求,都會攜帶上請求的 IP 地址放到請求頭中。
public class IpUtils {
/**
* 獲取ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request){
String ipAddress = null;
try {
ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
// 多次反向代理后會有多個ip值,第一個ip才是真實ip
if (ipAddress.indexOf(",") != -1) {
ipAddress = ipAddress.split(",")[0];
}
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
}catch (Exception e) {
log.error("IPUtils ERROR ",e);
}
return ipAddress;
}
對這里出現(xiàn)的幾個名詞解釋一下:
- X-Forwarded-For:一個 HTTP 擴(kuò)展頭部,主要是為了讓 Web 服務(wù)器獲取訪問用戶的真實 IP 地址。每個 IP 地址,每個值通過逗號+空格分開,最左邊是最原始客戶端的 IP 地址,中間如果有多層代理,每?層代理會將連接它的客戶端 IP 追加在 X-Forwarded-For 右邊。
- Proxy-Client-IP:這個一般是經(jīng)過 Apache http 服務(wù)器的請求才會有,用 Apache http 做代理時一般會加上 Proxy-Client-IP 請求頭
- WL-Proxy-Client-IP:也是通過 Apache http 服務(wù)器,在 weblogic 插件加上的頭。
- X-Real-IP:一般只記錄真實發(fā)出請求的客戶端IP
- HTTP_CLIENT_IP:代理服務(wù)器發(fā)送的HTTP頭。如果是“超級匿名代理”,則返回none值。
這里,要著重介紹一下Ip2region項目。
github地址:github.com/lionsoul201…


一個準(zhǔn)確率 99.9% 的離線 IP 地址定位庫,0.0x 毫秒級查詢,ip2region.db 數(shù)據(jù)庫只有數(shù)MB,提供了 java,php,c,python,nodejs,golang,c# 等查詢綁定和Binary,B樹,內(nèi)存三種查詢算法。
內(nèi)置的三種查詢算法
全部的查詢客戶端單次查詢都在 0.x 毫秒級別,內(nèi)置了三種查詢算法
- memory 算法:整個數(shù)據(jù)庫全部載入內(nèi)存,單次查詢都在0.1x毫秒內(nèi),C語言的客戶端單次查詢在0.00x毫秒級別。
- binary 算法:基于二分查找,基于ip2region.db文件,不需要載入內(nèi)存,單次查詢在0.x毫秒級別。
- b-tree 算法:基于btree算法,基于ip2region.db文件,不需要載入內(nèi)存,單詞查詢在0.x毫秒級別,比binary算法更快。
使用方法
1、引入ip2region依賴
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
2、下載倉庫中的ip2region.db 文件,放到工程resources目錄下

3、編寫方法加載ip2region文件,對用戶ip地址進(jìn)行轉(zhuǎn)換。
/**
* 獲取ip屬地
* @param ip
* @return
* @throws Exception
*/
public static String getCityInfo(String ip) throws Exception {
//獲得文件流時,因為讀取的文件是在打好jar文件里面,不能直接通過文件資源路徑拿到文件,但是可以在jar包中拿到文件流
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("ip2region.db");
Resource resource = resources[0];
InputStream is = resource.getInputStream();
File target = new File("ip2region.db");
FileUtils.copyInputStreamToFile(is, target);
is.close();
if (StringUtils.isEmpty(String.valueOf(target))) {
log.error("Error: Invalid ip2region.db file");
return null;
}
DbConfig config = new DbConfig();
DbSearcher searcher = new DbSearcher(config, String.valueOf(target));
//查詢算法
//B-tree, B樹搜索(更快)
int algorithm = DbSearcher.BTREE_ALGORITHM;
try {
//define the method
Method method;
method = searcher.getClass().getMethod("btreeSearch", String.class);
DataBlock dataBlock;
if (!Util.isIpAddress(ip)) {
log.error("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;
}
4、由于 ip 屬地在國內(nèi)的話,只會展示省份,而國外的話,只會展示國家。所以我們還需要對這個方法進(jìn)行一下封裝,得到獲取 IP 屬地的信息。
public static String getIpPossession(String ip) throws Exception {
String cityInfo = IpUtils.getCityInfo(ip);
if (!StringUtils.isEmpty(cityInfo)) {
cityInfo = cityInfo.replace("|", " ");
String[] cityList = cityInfo.split(" ");
if (cityList.length > 0) {
// 國內(nèi)的顯示到具體的省
if ("中國".equals(cityList[0])) {
if (cityList.length > 1) {
return cityList[1];
}
}
// 國外顯示到國家
return cityList[0];
}
}
return "未知";
}
5、編寫測試類。
public static void main(String[] args) throws Exception {
//國內(nèi)ip
String ip1 = "220.248.12.158";
String cityInfo1 = IpUtils.getCityInfo(ip1);
System.out.println(cityInfo1);
String address1 = IpUtils.getIpPossession(ip1);
System.out.println(address1);
//國外ip
String ip2 = "67.220.90.13";
String cityInfo2 = IpUtils.getCityInfo(ip2);
System.out.println(cityInfo2);
String address2 = IpUtils.getIpPossession(ip2);
System.out.println(address2);
}
6、測試結(jié)果

項目用到的全部依賴
想了解的小伙伴可以學(xué)習(xí)一下!
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>以上就是Java根據(jù)ip地址獲取歸屬地實例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java根據(jù)ip獲取歸屬地的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Day14基礎(chǔ)不牢地動山搖-Java基礎(chǔ)
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08
基于Java網(wǎng)絡(luò)編程和多線程的多對多聊天系統(tǒng)
這篇文章主要介紹了基于Java網(wǎng)絡(luò)編程和多線程的多對多聊天系統(tǒng),文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java網(wǎng)絡(luò)編程的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
java安全?ysoserial?CommonsCollections1示例解析
這篇文章主要介紹了java安全?ysoserial?CommonsCollections1示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Spring Boot 項目做性能監(jiān)控的操作流程
這篇文章主要介紹了Spring Boot 項目如何做性能監(jiān)控,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
反射機(jī)制:getDeclaredField和getField的區(qū)別說明
這篇文章主要介紹了反射機(jī)制:getDeclaredField和getField的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
BootStrap Jstree 樹形菜單的增刪改查的實現(xiàn)源碼
這篇文章主要介紹了BootStrap Jstree 樹形菜單的增刪改查的實現(xiàn)源碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-02-02

