獲取訪問者真實ip地址?我覺得不可能

天下沒有收費的bug發表於2021-08-26

我們真的能通過請求來獲取使用者真實的ip地址嘛?

答案是不能,如果能,肯定是我學的不夠深入,歡迎交流指正。


 

那麼寫這篇文章的意義是什麼?我們接著往下看。

IP地址相當於電腦在網路上的身份證,但事實上IP地址卻沒有想象中那麼簡單,IP也分很多種,比如經常提到內網IP和外網IP。

外網ip和內網ip的區別是什麼?

因為這個網路太複雜,講解時牽扯到的東西太多,沒法簡單地描述,長篇大論對初學者是極不友好的;如果非要簡單地講解又必然會有遺漏,可能會對你以後產生誤導。

我自己也對這個是比較模糊的,我就按照自己的理解大概解釋一遍。但是我講的不一定是對的。

我家裡的路由器是電信的,路由器上面連線一根電信的電話線,這跟電話線是要我家去電信繳費,才能上網。我家路由器能上網之後,就能開啟wifi,我的其他裝置就能通過wifi連線上這臺路由器。

 


 

上面介紹了個大概,接下來說說java端如何獲取訪問者 IP ?,先上程式碼

@Slf4j
public class IpUtil {

    /**
     * 獲取使用者ip地址
     *
     * @param request 請求
     * @return ip地址
     */
    public static String getIpAddr(HttpServletRequest request) {

        String ipAddress = request.getHeader("X-Original-Forwarded-For");
        log.info("X-Original-Forwarded-For:獲取的ipAddress{}",ipAddress);

        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("X-Forwarded-For");
            log.info("X-Forwarded-For:獲取的ipAddress{}",ipAddress);
        }
        // 對於通過多個代理的情況,因為ip長度最大為15,如果超過了15就代表有1個以上的ip,這時我們就取第一個,多個IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) {
            log.info("多個ip情況:獲取的ipAddress:{}",ipAddress);
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }

        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            // apache http伺服器的請求
            ipAddress = request.getHeader("Proxy-Client-IP");
            log.info("Proxy-Client-IP:獲取的ipAddress{}",ipAddress);
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            // apache http伺服器的請求,使用weblogic外掛加上的頭
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
            log.info("WL-Proxy-Client-IP:獲取的ipAddress{}",ipAddress);
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            // 有些代理伺服器會加上此請求頭
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
            log.info("HTTP_CLIENT_IP:獲取的ipAddress{}",ipAddress);
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {

            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
            log.info("HTTP_X_FORWARDED_FOR:獲取的ipAddress{}",ipAddress);
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            // nginx代理伺服器會加上此請求頭
            ipAddress = request.getHeader("X-Real-IP");
            log.info("X-Real-IP:獲取的ipAddress{}",ipAddress);
        }

        // 沒有使用代理的情況
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            log.info("request.getRemoteAddr()------->{}",ipAddress);
        }

        // 如果用localhost來訪問會返回0:0:0:0:0:0:0:1,這裡我們將它轉換為127.0.0.1
        return "0:0:0:0:0:0:0:1".equals(ipAddress)?"127.0.0.1":ipAddress;
    }

}

這段程式碼看起來很長,其實有用的部分就是 request.getRemoteAddr();

其他情況,都是根據目前常用的代理商使用的請求頭型別來獲取ip地址。

比如以下幾種情況:

X-Original-Forwarded-For

Proxy-Client-IP

HTTP_CLIENT_IP

HTTP_X_FORWARDED_FOR

這些都是一些常用的,因為這個請求頭型別是可以自定義的,所以我們根據請求頭型別獲取ip地址是不現實的。

但是為什麼程式碼裡面要寫呢?人情世故嘛,萬一公司是按程式碼行數算工資的呢。哈哈,開個玩笑,已知的代理請求頭,我們還是需要寫上去的。


 

不結合客戶使用場景的光討論技術都是耍流氓。下面我們重點說一下使用場景。

1、上面那段程式碼部署在外網環境,那麼獲取到的就是外網的ip,要是別人沒有經過偽裝,恭喜你,成功拿到他的ip。要是別人特意偽裝,你拿到這個ip或許是假的,沒有任何意義。

2、上面那段程式碼部署在內網環境,那麼獲取到的就是內網ip,這個內網ip,就能查出具體某臺電腦,也能在內網中遠端控制你的電腦。

上面兩個場景都是人畜無害。

一般是第二種情況使用的多。

比如你的公司有20層,公司所有員工都是用內網,你在20樓,你的同事在1樓,恰好你們公司沒有電梯,此時你想控制他的電腦,幫她處理一些事情,你就可以讓她直接訪問那段程式碼,獲取到她的ip地址。當然,程式碼肯定是封裝成一個介面,不然妹子怎麼訪問。

什麼?遠端不用向日葵?TV?QQ?

哈哈,是我格局小了。

什麼?直接叫妹子從電腦檢視ip地址發給我?

哈哈,是我格局小了。我就怕妹子不會看啊。

難不成讓她自己百度怎麼檢視ip地址?老實說,有這種想法的是不是還單身?


 

好了,對於獲取ip地址,網上也是各種形形色色,但是要明確使用場景,如果你真的想通過一個ip地址去追尋到一個人或者一臺電腦,在公網環境下,有點不現實。但是在內網環境中,這個確實可行。

 

相關文章