顯示IP歸屬地原理&Java實現(xiàn)IP歸屬地顯示功能

上個星期開始伞鲫,微博戏售、抖音、公眾號等多個平臺紛紛上線了 IP 歸屬地功能圣絮。

我想很多小伙伴會好奇互聯(lián)網(wǎng)平臺商們是怎么通過 IP 定位到我們所屬地區(qū)的祈惶?這背后的原理是什么?IP 歸屬地背后又有哪些實際的應(yīng)用扮匠?

所以捧请,今天我們就來聊聊 IP 歸屬地背后的技術(shù)原理

如何通過 IP 找到地址棒搜?

在我們印象中疹蛉,我們都知道可以通過 IP 地址找到某個人。但當(dāng)我們細(xì)想一下力麸,我們會發(fā)現(xiàn)其實 IP 地址與地理位置并不是直接相關(guān)的可款。那我們到底是如何通過 IP 地址找到地址的呢?

答案是:通過 自治系統(tǒng)(Autonomous System)克蚂。

互聯(lián)網(wǎng)是由不同網(wǎng)絡(luò)組成的網(wǎng)絡(luò)闺鲸,自治系統(tǒng)是組成 Internet 的大型網(wǎng)絡(luò),連接到 Internet 的每臺計算機(jī)或設(shè)備都連接到一個 AS陨舱。而每一個自治系統(tǒng)都會有一個編碼翠拣,我們稱之為 ASN版仔。


可以認(rèn)為 AS 類似于一個城鎮(zhèn)的郵局游盲。

郵件從一個郵局到另一個郵局,直到到達(dá)正確的城鎮(zhèn)為止蛮粮,然后該城鎮(zhèn)的郵局將在該城鎮(zhèn)內(nèi)傳遞郵件益缎。每個 AS 都控制一組特定的 IP 地址,就像每個鎮(zhèn)的郵局負(fù)責(zé)將郵件傳遞到該鎮(zhèn)內(nèi)的所有地址一樣然想。

通常莺奔,每個 AS 由單個大型組織(例如 Internet 服務(wù)提供商(ISP)、大型企業(yè)技術(shù)公司变泄、大學(xué)或政府機(jī)構(gòu))運營令哟。

到這里恼琼,我們可以捋清楚這樣一個邏輯關(guān)系:IP地址 -> 地址塊 -> 自治網(wǎng)絡(luò)編碼(ASN) -> 組織 -> 國家

通過 IP 地址屏富,我們就可以定位到一個大致的地理位置晴竞,例如:北京朝陽區(qū)、深圳南山區(qū)等狠半。例如我現(xiàn)在的 IP 地址就歸屬于編碼為 AS4xxx 這個自治網(wǎng)絡(luò)噩死,通過這個 ASN 可以知道位置在中國深圳,這個 ASN 編碼所屬的組織為 中國電信神年。

但是通過 ASN 也只能是找到縣級或者區(qū)級的地理位置已维,再細(xì)的位置就找不到了。

但怎么有些時候同學(xué)說:他被查水表了已日,直接定位到某個單元某一戶呢垛耳?其實原理也很簡單!上面我們說到可以根據(jù) IP 地址定位到 ASN 所屬組織飘千,而 ASN 所屬組織在進(jìn)行 IP 地址分配的時候艾扮,都是會進(jìn)行 IP 地址分配記錄的。

某個 IP 地址分配給了誰占婉,都記錄得一清二楚泡嘴。因此警察叔叔想找你喝茶,那還不是一抓一個準(zhǔn)逆济。

但要提示一下的是酌予,并不是誰都有那個權(quán)限去運營商查詢這些數(shù)據(jù)。所以那些說可以爬著網(wǎng)線找到你的人奖慌,基本上可以忽略抛虫,都是在嚇唬你。只有警察叔叔立案简僧,并且出示相關(guān)手續(xù)之后才可以進(jìn)行數(shù)據(jù)查詢建椰。

IP 地址的隱私問題

那是不是只有運營商才能查到某個人的住址信息呢?

在大數(shù)據(jù)時代的今天岛马,各種互聯(lián)網(wǎng)應(yīng)用搜集了大量的數(shù)據(jù)信息棉姐,它們其實也可以根據(jù)這些信息,推斷出某個人的大致地址位置啦逆。

例如百度地圖會一直用 App SDK 以及網(wǎng)頁的方式記錄 IP 和地址位置的關(guān)聯(lián)伞矩,并允許反向查詢,也就是可以根據(jù) IP 地址反向查詢到某個位置夏志,這個數(shù)據(jù)精度可能精確到幾百米乃坤。

其實不止國內(nèi)的公司會這么做,其實國外的公司同樣也會這么做,就比如 Google 也做了湿诊。

只是國外對這方面控制得非常嚴(yán)格狱杰,因此它們會比較明確地披露所使用的用戶隱私數(shù)據(jù),并且還提供了對應(yīng)功能可以讓用戶關(guān)閉厅须。

有朋友說了浦旱,那我可以改變 IP,那是不是某些 App 就不知道我的精確位置了呀九杂?其實并不是的颁湖,因為你的鄰居可以出賣了你

某些 App 發(fā)現(xiàn)例隆,鄰居周圍的 WIFI甥捺、藍(lán)牙等和你的非常像。而且當(dāng)某個 WiFi 信號消失時镀层,鄰居那邊的也同步消失了镰禾。那么他們就可以猜測,你隱藏了自己的真實 IP唱逢,你的地理位置和鄰居的非常近吴侦。這就是大數(shù)據(jù)時代背景下的應(yīng)用。

因此坞古,當(dāng)某些設(shè)備彈出提示「是否允許掃描本地設(shè)備」時备韧,你就要謹(jǐn)慎選擇了

如果不是內(nèi)網(wǎng) NAS 或者投屏類的痪枫,基本上沒有必要允許這個操作织堂,這個操作都是在盜取你的個人隱私信息。如果你允許了這個操作奶陈,那他就會開始掃描整個局域網(wǎng)的設(shè)備信息易阳,然后記錄下來。最終吃粒,其會將你的 IP 地址潦俺、手機(jī) IMEI、WiFi 等信息匯總起來徐勃,從而做一些商業(yè)化的信息事示。例如 ,你在電腦上搜了下房子這個關(guān)鍵詞疏旨,等會你刷手機(jī)抖音就會給你推送房地產(chǎn)廣告很魂。

連接本地網(wǎng)絡(luò)設(shè)備

說白了扎酷,「查找并連接到本地網(wǎng)絡(luò)上的設(shè)備」的使用為跨平臺廣告提供了方便檐涝,而從個人信息保護(hù)和隱私保護(hù)體驗的角度來講,這種對設(shè)備的監(jiān)控、跟蹤可能會給用戶帶來擔(dān)憂谁榜。

接下來使用Java實現(xiàn)IP歸屬地顯示功能

工具類
package com.p.h.utils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 獲取IP方法
 */
public class IpUtils {
    public static String getIpAddr(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    public static boolean internalIp(String ip) {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }

    private static boolean internalIp(byte[] addr) {
        if (StringUtils.isNull(addr) || addr.length < 2) {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                switch (b1) {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }

    /**
     * 將IPv4地址轉(zhuǎn)換成字節(jié)
     *
     * @param text IPv4地址
     * @return byte 字節(jié)
     */
    public static byte[] textToNumericFormatV4(String text) {
        if (text.length() == 0) {
            return null;
        }

        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try {
            long l;
            int i;
            switch (elements.length) {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L)) {
                        return null;
                    }
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L)) {
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        } catch (NumberFormatException e) {
            return null;
        }
        return bytes;
    }

    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
        }
        return "127.0.0.1";
    }

    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
        }
        return "未知";
    }
}
獲取地址類
package com.p.h.utils;

import com.alibaba.fastjson.JSONObject;
import com.fish.common.constant.Constants;
import com.fish.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 獲取地址類
 */
public class AddressUtils {
    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);

    // IP地址查詢
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";

    // 未知地址
    public static final String UNKNOWN = "XX XX";

    public static String getRealAddressByIP(String ip) {
        String address = UNKNOWN;
        // 內(nèi)網(wǎng)不查詢
        if (IpUtils.internalIp(ip)) {
            return "內(nèi)網(wǎng)IP";
        }
        try {
            String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
            if (StringUtils.isEmpty(rspStr)) {
                log.error("獲取地理位置異常 {}", ip);
                return UNKNOWN;
            }
            JSONObject obj = JSONObject.parseObject(rspStr);
            String region = obj.getString("pro");
            String city = obj.getString("city");
            return String.format("%s %s", region, city);
        } catch (Exception e) {
            log.error("獲取地理位置異常 {}", e);
        }
        return address;
    }
}

.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幅聘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窃植,更是在濱河造成了極大的恐慌帝蒿,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巷怜,死亡現(xiàn)場離奇詭異葛超,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)延塑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進(jìn)店門绣张,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人关带,你說我怎么就攤上這事侥涵。” “怎么了宋雏?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵芜飘,是天一觀的道長。 經(jīng)常有香客問我磨总,道長嗦明,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任蚪燕,我火速辦了婚禮招狸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邻薯。我一直安慰自己裙戏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布厕诡。 她就那樣靜靜地躺著累榜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灵嫌。 梳的紋絲不亂的頭發(fā)上壹罚,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機(jī)與錄音寿羞,去河邊找鬼猖凛。 笑死,一個胖子當(dāng)著我的面吹牛绪穆,可吹牛的內(nèi)容都是我干的辨泳。 我是一名探鬼主播虱岂,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼菠红!你這毒婦竟也來了第岖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤试溯,失蹤者是張志新(化名)和其女友劉穎蔑滓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遇绞,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡键袱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摹闽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杠纵。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钩骇,靈堂內(nèi)的尸體忽然破棺而出比藻,到底是詐尸還是另有隱情,我是刑警寧澤倘屹,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布银亲,位于F島的核電站,受9級特大地震影響纽匙,放射性物質(zhì)發(fā)生泄漏务蝠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一烛缔、第九天 我趴在偏房一處隱蔽的房頂上張望馏段。 院中可真熱鬧,春花似錦践瓷、人聲如沸院喜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喷舀。三九已至,卻和暖如春淋肾,著一層夾襖步出監(jiān)牢的瞬間硫麻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工樊卓, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留拿愧,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓碌尔,卻偏偏與公主長得像浇辜,于是被迫代替她去往敵國和親券敌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容