Java根據ip地址獲取歸屬地實例詳解
引言
最近,各大平臺都新增瞭評論區顯示發言者ip歸屬地的功能,例如嗶哩嗶哩,微博,知乎等等。
Java 中是如何獲取 IP 屬地的
主要分為以下幾步
- 通過 HttpServletRequest 對象,獲取用戶的 IP 地址
- 通過 IP 地址,獲取對應的省份、城市
首先需要寫一個 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; }
對這裡出現的幾個名詞解釋一下:
- X-Forwarded-For:一個 HTTP 擴展頭部,主要是為瞭讓 Web 服務器獲取訪問用戶的真實 IP 地址。每個 IP 地址,每個值通過逗號+空格分開,最左邊是最原始客戶端的 IP 地址,中間如果有多層代理,每⼀層代理會將連接它的客戶端 IP 追加在 X-Forwarded-For 右邊。
- Proxy-Client-IP:這個一般是經過 Apache http 服務器的請求才會有,用 Apache http 做代理時一般會加上 Proxy-Client-IP 請求頭
- WL-Proxy-Client-IP:也是通過 Apache http 服務器,在 weblogic 插件加上的頭。
- X-Real-IP:一般隻記錄真實發出請求的客戶端IP
- HTTP_CLIENT_IP:代理服務器發送的HTTP頭。如果是“超級匿名代理”,則返回none值。
這裡,要著重介紹一下Ip2region項目。
github地址:github.com/lionsoul201…
一個準確率 99.9% 的離線 IP 地址定位庫,0.0x 毫秒級查詢,ip2region.db 數據庫隻有數MB,提供瞭 java,php,c,python,nodejs,golang,c# 等查詢綁定和Binary,B樹,內存三種查詢算法。
內置的三種查詢算法
全部的查詢客戶端單次查詢都在 0.x 毫秒級別,內置瞭三種查詢算法
- memory 算法:整個數據庫全部載入內存,單次查詢都在0.1x毫秒內,C語言的客戶端單次查詢在0.00x毫秒級別。
- binary 算法:基於二分查找,基於ip2region.db文件,不需要載入內存,單次查詢在0.x毫秒級別。
- b-tree 算法:基於btree算法,基於ip2region.db文件,不需要載入內存,單詞查詢在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地址進行轉換。
/** * 獲取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 屬地在國內的話,隻會展示省份,而國外的話,隻會展示國傢。所以我們還需要對這個方法進行一下封裝,得到獲取 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) { // 國內的顯示到具體的省 if ("中國".equals(cityList[0])) { if (cityList.length > 1) { return cityList[1]; } } // 國外顯示到國傢 return cityList[0]; } } return "未知"; }
5、編寫測試類。
public static void main(String[] args) throws Exception { //國內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、測試結果
項目用到的全部依賴
想瞭解的小夥伴可以學習一下!
<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根據ip地址獲取歸屬地實例詳解的詳細內容,更多關於Java根據ip獲取歸屬地的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Java獲取用戶IP屬地模擬抖音詳解
- Spring Boot快速實現 IP地址解析的示例詳解
- springboot 獲取訪問接口的請求的IP地址的實現
- java如何通過IP解析地理位置
- SpringBoot使用ip2region獲取地理位置信息的方法