java IP歸屬地功能實現詳解
第一步:如何拿到用戶的真實IP
大傢都知道,我們一般想訪問公網,一般必須具備上網環境,那麼我們開通寬帶之後,運營商會給我們分配一個IP地址。一般IP地址我們都是自動分配的。所以我們不知道本機地址是什麼?想知道自己的ip公網地址,可以通過百度搜索IP查看自己的ip位置
那麼問題來瞭。百度是怎麼知道我的公網IP的?
一般情況,用戶訪問我們的服務網絡拓撲如下:
用戶通過域名或者IP訪問門戶,然後請求到後端服務。這樣的話後端服務就可以通過request.getRemoteAddr();方法獲取用戶的ip。
SpringBoot獲取IP如下:
@RestController public class IpController { @RequestMapping("/getIp") public String hello(HttpServletRequest request) { String ip = request.getRemoteAddr(); System.out.println(ip); return ip; } }
將服務部署到服務端,然後請求該接口,即可獲取IP信息,如下圖:
但是為什麼我們獲取的IP和百度搜出來的不一樣呢?
1.1內網IP和外網IP
打開電腦CMD,輸出ipconfig命令,查看本機的IP地址,發現我們本機地址和程序獲取的地址是一樣的。
其實,網絡也是分內網IP和公網IP的。內網也成局域網。對於像公司,學校這種一般內部建立自己的局域網,對內部的信息進行傳輸時,都是通過內網相互通訊,建立局域網內網通訊節省瞭公網IP資源,並且通信效率也有很大的提升。當然非局域網內的設備則無法向內網的設備發送信息。
但是機器想要訪問互聯網的資源時,則需要機器擁有外網帶寬,也就是我們所說的分配公網IP,負責也是無法訪問互聯網資源的。
因此,我們把服務部署在同一局域網內,客戶端使用內網進行通信,因此獲取的就是內網IP地址。但訪問百度是需要使用公網訪問,因此百度搜出來的IP就是公網IP地址。
1.2.為什麼有時候獲取到的客戶端IP有問題?
當我們興致勃勃的把IP獲取的功能搞上去之後,發現獲取的IP都是同一個?這是為什麼呢?不可能隻是一個用戶在訪問呀?查詢IP信息之後發現,原來是我們部署的一臺負載均衡的IP地址。
那麼後端服務獲取的地址都是負載均衡如nginx的地址。那麼怎麼透過負載均衡獲取真實的地址呢?
透明的代理服務器在將客戶端的訪問請求轉發到下一環節的服務器時,會在HTTP的請求頭中添加一條X-Forwarded-For記錄,用於記錄客戶端的IP,格式為X-Forwarded-For:客戶端IP。如果客戶端和服務器之間有多個代理服務器,則X-Forwarded-For記錄使用以下格式記錄客戶端IP和依次經過的代理服務器IP:X-Forwarded-For:客戶端IP, 代理服務器1的IP, 代理服務器2的IP, 代理服務器3的IP, ……。
因此,常見的Web應用服務器可以通過解析X-Forwarded-For記錄獲取客戶端真實IP。
public static String getIp(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } else if (ip.length() > 15) { //多次反向代理後會有多個ip值,第一個ip才是真實ip String[] ips = ip.split(","); for (int index = 0; index < ips.length; index++) { String strIp = ips[index]; ip = strIp; break; } } return ip; }
第二步:如何解析IP
IP來瞭,我們怎麼解析呢:
IP的解析一般都要借助第三方軟件使用瞭,第三方一般也分為離線庫和在線庫
- 離線庫支持的有如:IPIP,使用離線庫的好處是解析效率高,性能好,問題就是IP庫要經常更新。如果大傢需要我私信我可以提供給大傢比較新版本的ip庫。
- 在線庫則各大雲廠商接口能力都有支持。在線版本的好處是更新即時,問題就是接口查詢性能和使用TPS有要求。
以下演示借助IP庫離線IP解析方式:
借助IP庫就可以幫我們實現ip地址的解析。
public static void main(String[] args) { IpAddrInfo IpAddrInfo = IPAddr.getInstance().putLocInfo("114.103.71.226"); System.out.println(JSONObject.toJSONString(IpAddrInfo)); } public IpAddrInfo putLocInfo(String ip) { IpAddrInfo info = new IpAddrInfo(); if (StringUtils.isNotBlank(ip)) { try { DistrictInfo addrInfo = db.findInfo(ip, "CN"); info.setCity(addrInfo.getCityName()); info.setCountry(addrInfo.getCountryName()); info.setCountryCode(addrInfo.getChinaAdminCode()); info.setIsp(addrInfo.getIsp()); info.setLat(addrInfo.getLatitude()); info.setLon(addrInfo.getLongitude()); info.setProvince(addrInfo.getRegionName()); info.setTimeZone(addrInfo.getTimeZone()); System.out.println(addrInfo.toString()); } catch (IPFormatException e) { e.printStackTrace(); } catch (InvalidDatabaseException e) { e.printStackTrace(); } } return info; }
其實IP的定位解析其實就是一個巨大的位置庫,同時IP數量也是有限制的,因此同一個Ip也可能會分配到不同的區域,因此影響IP解析位置準確率的有幾個方面
1、位置庫不精準,導致解析偏差大或者地區字段確實
2、離線庫更新不及時 並且海外的一般有專門的離線庫去支持,使用同一套離線庫並不一定支持海外IP的解析,所以本次受影響最大的海外網紅門被解析到中國各個地區,被大傢認為造假,當然也包括真的有造假。
不過上線瞭這個功能也是有好處的,至少網絡不是法外之地,大傢也要有序的健康的沖浪,拒絕網絡暴力。
以上就是java IP歸屬地功能實現詳解的詳細內容,更多關於java IP歸屬地的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- springboot 獲取訪問接口的請求的IP地址的實現
- Spring Boot快速實現 IP地址解析的示例詳解
- java如何通過IP解析地理位置
- Java根據ip地址獲取歸屬地實例詳解
- 詳解SpringBoot中自定義和配置攔截器的方法