您可以通过JavaScript获取用户本地LAN IP地址吗?


102

我知道对此问题的最初反应是“否”和“无法完成”,并且“您不需要它,您做错了什么”。我正在尝试获取用户的LAN IP地址,并将其显示在网页上。为什么?因为这就是我正在处理的页面的全部内容,因此会显示有关您的尽可能多的信息,访问者:http : //www.whatsmyip.org/more-info-about-you/

因此,除了向用户展示以供参考之外,我实际上并没有对IP做任何事情。我曾经通过使用一个小的Java小程序来做到这一点。效果很好。但是这些天来,浏览器使您多次达成共识和信任,即使是最次要的Java applet也要运行,我宁愿完全不运行它。

所以有一阵子我刚刚摆脱了这个功能,但是如果可能的话我希望它回来。作为计算机顾问,我有时会不时使用这种东西。转到该网站查看网络正在运行的IP范围,比进入“系统偏好设置”,“网络”以及任何活动的接口都要快。

所以我想知道,希望是否有某种方法可以单独使用javascript?也许您可以访问一些新对象,类似于javascript询问浏览器在地球上的地理位置在哪里。客户端网络信息可能有相似之处吗?如果没有,也许还有其他方法可以完全做到这一点?我能想到的唯一方法是Java小程序或Flash对象。我宁愿不做任何一个。


1
你知道答案。为什么要问呢?用户不太可能允许Java小程序或Flash对象(可能只允许Internet上的新手使用)-因此,在通常情况下,这不是解决方案。ActiveX和附近的内容仅在IE中有效-因此,其他浏览器的用户将不会受到影响(而且,即使在IE中,也存在一项安全策略可以阻止网站进行令人讨厌的事情)
Alma Do

我的IP地址是通过HTTP_X_FORWARDED_FOR该页面捕获的,只是说出来。
tomdemuyt 2013年

50
那为什么要问?因为也许,也许,也许我什么都不知道。
l008com 2013年

1
这些家伙做到这一点: whatismyproxy.com
likebike

1
@likebike不错的一个。研究他们如何做到这一点。
多米尼克·塞里萨诺

Answers:


117

事实证明,最近对HTML5的WebRTC扩展允许javascript查询本地客户端IP地址。此处提供概念验证:http//net.ipcalf.com

此功能显然是设计使然,不是错误。但是,鉴于其有争议的性质,我将谨慎对待这种行为。尽管如此,我认为它完美而恰当地解决了您的预期目的(向用户显示他们的浏览器正在泄漏什么)。


1
这很有帮助。再次感谢!
Ansuraj Khadanga '16

7
这仅仅是工作的Chrome和Firefox,而不是在IE浏览器,边缘或Safari
阿里

我正在查找我的WAN IP,这个网站whatismyip.com也给了我我的本地IP,我想它与JS有关。
莎燕

@ali您是正确的,我上面提到的网站无法告诉我Edge上的本地IP。
Shayan

6
Google Chrome默认情况下隐藏本地IP。它显示类似于e87e041d-15e1-4662-adad-7a6601fca9fb.local的内容。可以通过在Chrome:// flags
中将

81

更新资料

该解决方案将不再起作用,因为浏览器正在修复WebRTC泄漏:有关该问题的更多信息,请阅读另一个问题:RTCIceCandidate不再返回IP


除了afourney的答案外,此代码还可在支持WebRTC(Chrome和Firefox)的浏览器中使用。我听说正在进行一项实现使站点请求IP的功能的动议(例如在用户的地理位置或用户媒体的情况下),尽管尚未在这两个浏览器中的任何一个中实现。

这是源代码的修改版本,减少了行数,不发出任何眩晕请求,因为您只希望本地IP而不是公共IP:

window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;//compatibility for Firefox and chrome
var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};      
pc.createDataChannel('');//create a bogus data channel
pc.createOffer(pc.setLocalDescription.bind(pc), noop);// create offer and set local description
pc.onicecandidate = function(ice)
{
 if (ice && ice.candidate && ice.candidate.candidate)
 {
  var myIP = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];
  console.log('my IP: ', myIP);   
  pc.onicecandidate = noop;
 }
};

我们正在创建一个虚拟对等连接,以供远程对等与我们联系。我们通常互相交换冰候选者,并阅读冰候选者,我们可以告诉用户ip。

您可以在-> 演示中找到演示


感谢您的美度!非常感激。
Sujay Phadke

1
@dampee-我相信Edge目前不支持数据通道。
MichaelB76

什么是伪数据通道?找不到对谷歌的任何参考
AmazingTurtle

2
注意createOffer API已切换到基于承诺的,而不是successCallback和failCallback作为参数,可以使这可能不会对新版本的工作,请参阅:developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/...
Dickeylth

10

的WebRTC API可用于检索客户端的本地IP。

但是,浏览器可能不支持它,或者客户端可能出于安全原因已将其禁用。无论如何,从长远来看,不应该依赖此“ hack”,因为将来可能会对其进行修补(请参阅Cullen Fluffy Jennings的回答)。

下面的ECMAScript 6代码演示了如何执行此操作。

/* ES6 */
const findLocalIp = (logInfo = true) => new Promise( (resolve, reject) => {
    window.RTCPeerConnection = window.RTCPeerConnection 
                            || window.mozRTCPeerConnection 
                            || window.webkitRTCPeerConnection;

    if ( typeof window.RTCPeerConnection == 'undefined' )
        return reject('WebRTC not supported by browser');

    let pc = new RTCPeerConnection();
    let ips = [];

    pc.createDataChannel("");
    pc.createOffer()
     .then(offer => pc.setLocalDescription(offer))
     .catch(err => reject(err));
    pc.onicecandidate = event => {
        if ( !event || !event.candidate ) {
            // All ICE candidates have been sent.
            if ( ips.length == 0 )
                return reject('WebRTC disabled or restricted by browser');

            return resolve(ips);
        }

        let parts = event.candidate.candidate.split(' ');
        let [base,componentId,protocol,priority,ip,port,,type,...attr] = parts;
        let component = ['rtp', 'rtpc'];

        if ( ! ips.some(e => e == ip) )
            ips.push(ip);

        if ( ! logInfo )
            return;

        console.log(" candidate: " + base.split(':')[1]);
        console.log(" component: " + component[componentId - 1]);
        console.log("  protocol: " + protocol);
        console.log("  priority: " + priority);
        console.log("        ip: " + ip);
        console.log("      port: " + port);
        console.log("      type: " + type);

        if ( attr.length ) {
            console.log("attributes: ");
            for(let i = 0; i < attr.length; i += 2)
                console.log("> " + attr[i] + ": " + attr[i+1]);
        }

        console.log();
    };
} );

请注意,我写return resolve(..)return reject(..)作为快捷方式。这两个函数均不返回任何内容。

然后,您可能会有以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Local IP</title>
</head>
<body>
    <h1>My local IP is</h1>
    <p id="ip">Loading..</p>
    <script src="ip.js"></script>
    <script>
    let p = document.getElementById('ip');
    findLocalIp().then(
        ips => {
            let s = '';
            ips.forEach( ip => s += ip + '<br>' );
            p.innerHTML = s;
        },
        err => p.innerHTML = err
    );
    </script>
</body>
</html>

9

我清理了mido的帖子,然后清理了他们发现的功能。这将返回falsearray。测试时请记住,您需要在Web开发人员控制台中折叠数组,否则它的非直观默认行为可能会欺骗您,使您认为它返回的是empty array

function ip_local()
{
 var ip = false;
 window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || false;

 if (window.RTCPeerConnection)
 {
  ip = [];
  var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};
  pc.createDataChannel('');
  pc.createOffer(pc.setLocalDescription.bind(pc), noop);

  pc.onicecandidate = function(event)
  {
   if (event && event.candidate && event.candidate.candidate)
   {
    var s = event.candidate.candidate.split('\n');
    ip.push(s[0].split(' ')[4]);
   }
  }
 }

 return ip;
}

另外,请记住,这不是CSS之类的新东西,border-radius尽管IE11和更早版本完全支持这些功能之一。始终使用对象检测,在相当老版本的浏览器(例如Firefox 4,IE9,Opera 12.1)中进行测试,并确保您的新脚本没有破坏您的新代码。另外总是检测标准的代码第一,所以如果有什么用说CSS前缀检测标准的非前缀码第一,然后回落作为长期的支持最终将被标准化为它的存在的其余部分。


你重新声明ip- 3号线和8行
user2757813

直到Internet Explorer 15(或“ Edge 15”)才引入@Anu WebRTC,所以没有。这就是为什么在上面第四行中,如果不存在任何对象,则该函数将返回false的原因。如果有另一种方法可以在IE中实现此功能,那么我目前尚不了解。
约翰·约翰(John

@John-我们如何将返回值传递给php变量?通过隐藏的帖子?
MarcoZen

@MarcoZen <input name="example1" type="hidden" value="whatever" />在这种情况下,您可以使用或使用AJAX POST。我强烈建议您ajax()在此处研究我的函数:jabcreations.com/docs/javascript
约翰

刚刚发现,某些浏览器(例如Chrome)现在阻止提供IP-如果未请求视频/音频权限,则相同的代码现在可以解析为mDNS主机名。参见groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger

6

function getUserIP(onNewIP) { //  onNewIp - your listener function for new IPs
  //compatibility for firefox and chrome
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
  var pc = new myPeerConnection({
      iceServers: []
    }),
    noop = function() {},
    localIPs = {},
    ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
    key;

  function iterateIP(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  onNewIP
  //create a bogus data channel
  pc.createDataChannel("");

  // create offer and set local description
  pc.createOffer().then(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(iterateIP);
    });

    pc.setLocalDescription(sdp, noop, noop);
  }).catch(function(reason) {
    // An error occurred, so handle the failure to connect
  });

  //listen for candidate events
  pc.onicecandidate = function(ice) {
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(iterateIP);
  };
}
getUserIP(console.log)


请使用编辑器选项来适当设置代码格式。
31piy

3
如果您不仅要删除一些代码,而且要解释他和您的代码中发生的事情,那将是很好的。它可以帮助问题作者和其他用户。如果有效,那很好,但是在我看来,了解原因甚至更重要。
davejal

任何与IE兼容的解决方案?
Anu

1
该评论是本文的复制粘贴:ourcodeworld.com/articles/read/257/…–
Darkshifty

刚刚发现,某些浏览器(例如Chrome)现在阻止提供IP-如果未请求视频/音频权限,则相同的代码现在可以解析为mDNS主机名。参见groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU
Christoph Bimminger

5

镀铬76+

去年,我使用Linblow的答案(2018年10月19日)通过javascript成功发现了我的本地IP。但是,最近的Chrome更新(76?)已经采用了这种方法,因此它现在返回的是模糊IP,例如:1f4712db-ea17-4bcf-a596-105139dfd8bf.local

如果您完全控制浏览器,则可以通过在Chrome标记中将其关闭,方法是在地址栏中输入以下内容来撤消此行为:

chrome://flags

和禁用标志 Anonymize local IPs exposed by WebRTC

就我而言,我需要TamperMonkey脚本的IP来确定我当前的位置并根据我的位置执行不同的操作。我也可以完全控制自己的浏览器设置(无公司政策等)。所以对我来说,更改chrome://flags设置可以解决问题。

资料来源:

https://groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU

https://codelabs.developers.google.com/codelabs/webrtc-web/index.html


该标志可能会消失。当前看来,扩展名仍然可以获取IP,因此您可以尝试从后台脚本中获取它。长期来看,所有赌注都没有。
菲利普·汉克

1
根据groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU的规定,如果您实施要求音频/视频许可的解决方案,则仍应返回IP。
Christoph Bimminger


0

一个RTCPeerConnection可以使用。在getUserMedia需要许可的 Chrome之类的浏览器中,我们只能检测可用的输入设备并请求它们。

const internalIp = async () => {
    if (!RTCPeerConnection) {
        throw new Error("Not supported.")
    }

    const peerConnection = new RTCPeerConnection({ iceServers: [] })

    peerConnection.createDataChannel('')
    peerConnection.createOffer(peerConnection.setLocalDescription.bind(peerConnection), () => { })

    peerConnection.addEventListener("icecandidateerror", (event) => {
        throw new Error(event.errorText)
    })

    return new Promise(async resolve => {
        peerConnection.addEventListener("icecandidate", async ({candidate}) => {
            peerConnection.close()

            if (candidate && candidate.candidate) {
                const result = candidate.candidate.split(" ")[4]
                if (result.endsWith(".local")) {
                    const inputDevices = await navigator.mediaDevices.enumerateDevices()
                    const inputDeviceTypes = inputDevices.map(({ kind }) => kind)

                    const constraints = {}

                    if (inputDeviceTypes.includes("audioinput")) {
                        constraints.audio = true
                    } else if (inputDeviceTypes.includes("videoinput")) {
                        constraints.video = true
                    } else {
                        throw new Error("An audio or video input device is required!")
                    }

                    const mediaStream = await navigator.mediaDevices.getUserMedia(constraints)
                    mediaStream.getTracks().forEach(track => track.stop())
                    resolve(internalIp())
                }
                resolve(result)
            }
        })
    })
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.