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

Posted

技术标签:

【中文标题】您可以通过 JavaScript 获取用户本地 LAN IP 地址吗?【英文标题】:Can You Get A Users Local LAN IP Address Via JavaScript? 【发布时间】:2013-12-10 06:38:10 【问题描述】:

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

所以我实际上并没有对 IP 做任何事情,除了将其展示给用户以供参考。我曾经使用一个小的 Java 小程序来做到这一点。它工作得很好。但是现在,浏览器让你多次点击同意和信任,即使是最次要的 java 小程序也能运行,我宁愿一个也不运行。

所以有一段时间我刚刚摆脱了这个功能,但如果可能的话我希望它回来。这是我,作为一名计算机顾问,实际上会不时使用的东西。访问该网站查看网络运行的 IP 范围比进入系统偏好设置、网络以及任何处于活动状态的界面要快。

所以我想知道,希望是否有某种方法可以仅在 javascript 中做到这一点?也许您可以访问一些新对象,类似于 javascript 可以询问浏览器的方式,地球上的地理位置在哪里。也许客户端网络信息有类似的东西?如果没有,也许还有其他方法可以完全做到这一点?我能想到的唯一方法是 java applet 或 flash 对象。我宁愿不做这两件事。

【问题讨论】:

你知道答案。那为什么还要问?用户不太可能允许 Java 小程序或 Flash 对象(可能只有那些刚接触 Internet 的人)——因此这不是常见情况下的解决方案。 ActiveX 和附近的东西只在 IE 中工作 - 因此,其他浏览器的用户不会受到影响(而且,更重要的是,即使在 IE 中也有一个安全策略可以防止网站做讨厌的事情) 我的 IP 地址是通过该页面上的HTTP_X_FORWARDED_FOR 捕获的,只是说。 那为什么要问呢?因为也许,只是也许,我什么都不知道。 这些人做到了:whatismyproxy.com @likebike 不错。调查他们是如何做到这一点的。 【参考方案1】:

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

这个功能显然是by design,并不是一个错误。然而,鉴于其有争议的性质,我会谨慎依赖这种行为。尽管如此,我认为它完美且适当地解决了您的预期目的(向用户揭示他们的浏览器正在泄漏什么)。

【讨论】:

这很有帮助。再次感谢! 它只适用于 chrome 和 firefox,而不适用于 IE、Edge 或 safari 我正在查找我的 WAN IP,这个网站whatismyip.com 也给了我我的本地 IP,我想这与 JS 有关。 谷歌浏览器默认隐藏本地IP。它显示类似于 e87e041d-15e1-4662-adad-7a6601fca9fb.local 的内容。通过在 Chrome://flags 中将变量 #enable-webrtc-hide-local-ips-with-mdns 设置为 disabled 可以更改此行为 这不起作用了,对象RTCSessionDescription返回0.0.0.0作为本地ipV4地址。它一直返回本地 IP,直到 Firefox 80 左右【参考方案2】:

更新

此解决方案将不再有效,因为浏览器正在修复 webrtc 泄漏:有关此问题的更多信息,请阅读另一个问题:RTCIceCandidate no longer returning IP


除了 afourney 的回答之外,此代码还适用于支持 WebRTC(Chrome 和 Firefox)的浏览器。我听说正在实施一项使网站请求 IP 的功能(例如在用户的地理位置或用户媒体的情况下),尽管它尚未在这两种浏览器中实现。

这是 source code 的修改版本,减少了行数,不会发出任何 stun 请求,因为您只需要本地 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;
 
;

我们正在创建一个虚拟对等连接,以便远程对等方与我们联系。我们一般会互相交换 ice 候选项,读取 ice 候选项就可以知道用户的 ip。

你可以在 --> Demo找到一个演示

【讨论】:

感谢美度!非常感谢。 @dampee - 我相信 Edge 目前不支持数据通道。 什么是虚假数据通道?在谷歌上找不到任何参考 请注意 createOffer api 已切换为基于 Promise 而不是 successCallback 和 failCallback 作为参数,因此这可能不适用于较新的版本,请参阅:developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…【参考方案3】:

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

但是浏览器可能不支持它,或者客户端可能出于安全原因禁用了它。无论如何,从长远来看,不应依赖这种“黑客”,因为它可能会在未来被修补(参见 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>

【讨论】:

【参考方案4】:

我清理了 mido 的帖子,然后清理了他们找到的功能。这将返回falsearray。在测试时记住您需要在 Web 开发人员控制台中折叠数组,否则它的非直观默认行为可能会欺骗您认为它正在返回一个空的 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)中进行测试,并确保您的新脚本不会破坏您的新代码。另外总是检测符合标准的代码first,所以如果有CSS前缀检测标准的非前缀代码first然后回退因为从长远来看,支持最终将在其存在的其余部分标准化。

【讨论】:

您正在重新声明 ip - 第 3 行和第 8 行。 @Anu WebRTC 直到 Internet Explorer 15(或“Edge 15”)才被引入,所以没有。这就是为什么在上面的第四行中,如果不存在任何对象,该函数将返回 false。如果在 IE 中还有其他方法可以实现这一点,那么我目前不知道。 @John - 我们如何将返回值传递给 php 变量?通过隐藏帖子? @MarcoZen 在这种情况下,您可以使用 &lt;input name="example1" type="hidden" value="whatever" /&gt; 或使用 AJAX POST。我强烈建议在这里学习我的ajax() 函数:jabcreations.com/docs/javascript 刚刚发现某些浏览器(例如 Chrome)现在阻止提供 IP - 如果未请求视频/音频权限,相同的代码现在解析为 mDNS 主机名。见groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU【参考方案5】:

Chrome 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,因此您可以尝试从后台脚本中获取它。不过,从长远来看,一切都结束了。 根据groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU,如果您实施请求音频/视频权限的解决方案,仍应返回IP。 出于完全相同的原因,我一直在寻找这个,当我阅读您帖子的最后一段时,我是在寻找这个。不敢相信这在 2020 年末仍然是可能的,但很高兴它是! +1 分享。【参考方案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)

【讨论】:

请使用编辑器选项适当地格式化您的代码。 如果您不仅要删除一些代码,而且还要解释他和您的代码中发生的事情,那就太好了。它可以帮助问题作者和其他用户。如果它有效就很好,但在我看来,知道为什么更重要。 任何 IE 兼容的解决方案? 评论是这篇文章的复制粘贴:ourcodeworld.com/articles/read/257/… 刚刚发现一些浏览器(例如 Chrome)现在阻止提供 IP - 如果没有请求视频/音频权限,相同的代码现在解析为 mDNS 主机名。见groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU【参考方案7】:

您可以在IETF SPEC on IP handling找到更多信息,了解浏览器可能会增加哪些限制来缓解这种情况,IETF 正在做什么,以及为什么需要这样做。

【讨论】:

【参考方案8】:

现在支持internal-ip!

可以使用RTCPeerConnection。在像 Chrome 这样的浏览器中,getUserMedia permission is required,我们只能检测可用的输入设备并请求它们。

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)
            
        )
    )

【讨论】:

谢谢!这正是我所需要的。只是 Javascript,没有 HTML 和可理解的代码 :) 哈哈哈,所以我不得不为此允许访问我的麦克风,但它最终吐出了 hyperv vswitch 的 IP 地址【参考方案9】:

尝试使用操作系统包查找wifi ip。

const os = require('os');
console.log('IP Address: ' + JSON.stringify(os.networkInterfaces()['Wi-Fi']).match(/"192.168.\d+.\d+"/g)[0])

【讨论】:

以上是关于您可以通过 JavaScript 获取用户本地 LAN IP 地址吗?的主要内容,如果未能解决你的问题,请参考以下文章

用javaScript怎么样获取用户本地的ip地址?

获取日期和货币格式

使用JavaScript从组件外部遍历本地DOM

使用 Javascript 使用 ejabberd 获取在线用户列表?

IOS获取本地通知的属性

JavaScript:获取用户位置