你能通过 JavaScript 获得一个用户局域网 IP 地址吗?

我知道对这个问题的最初反应是“不”、“这不可能”和“你不应该需要它,你做错了”。我试图做的是得到用户的局域网 IP 地址,并显示在网页上。为什么?因为这就是我正在创作的网页的全部内容,尽可能多地展示关于你这个访问者的信息: Https://www.whatsmyip.org/more-info-about-you/

So I'm not actually DOING anything with the IP, other than showing it to the user for informational purposes. I used to do this by using a small Java applet. It worked pretty well. But these days, browser make you hit agree and trust so many times, to run even the most minor java applet, that I'd rather not run one at all.

所以有一段时间我没有使用这个特性,但是如果可能的话,我希望它能够恢复。作为一名计算机顾问,我时不时会用到它。进入这个网站查看网络运行的 IP 范围要比进入系统首选项、网络以及任何活动的接口都要快。

所以我想知道,希望,是否有某种方法可以单独使用 javascript?也许你可以访问一些新的对象,类似于 javascript 询问浏览器地理位置的方式。也许对于客户网络信息也有类似的东西?如果没有,也许还有其他完全不同的方法来做到这一点?我能想到的唯一方法是使用 java applet 或 flash 对象。这两件事我都不想做。

253272 次浏览

As it turns out, the recent WebRTC extension of HTML5 allows javascript to query the local client IP address. A proof of concept is available here: http://net.ipcalf.com

这个特性显然是 故意的,而不是 bug。然而,鉴于这种行为具有争议性,我对依赖这种行为持谨慎态度。尽管如此,我认为它完美而恰当地解决了您的预期目的(向用户揭示他们的浏览器正在泄漏的内容)。

更新

这个解决方案将不再有效,因为浏览器正在修复 webtc 泄漏: 更多信息请阅读另一个问题: RTCIceCandidate 不再返回 IP 地址


In addition to afourney's answer this code works in browsers that support WebRTC (Chrome and Firefox). I heard there is a movement going on to implement a feature that makes sites request the IP (like in case of user's geo-location or user-media) though it has yet to be implemented in either of those browsers.

这里是一个修改版本的 源代码,减少了线路,没有发出任何眩晕请求,因为你只想要本地 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 地址。

你可以在—— > 演示找到一个演示

您可以找到更多关于浏览器可能增加哪些限制以减轻这种影响的信息,以及 IETF 正在对此做些什么,以及为什么需要在 关于 IP 处理的 IETF 规范上这样做

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)

I cleaned up mido's post and then cleaned up the function that they found. This will either return false or an array. When testing 记住 that you need to collapse the array in the web developer console otherwise it's nonintuitive default behavior may deceive you in to thinking that it is returning an 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新的,虽然这些位,是完全的 not支持 IE11和更老的。使用目标检测,在比较老的浏览器上测试(比如 Firefox 4,IE9,Opera 12.1) ,确保你的新脚本没有破坏你的新代码。此外,一直都是检测标准兼容代码 第一,所以如果有一个 CSS 前缀检测标准的无前缀代码 第一,然后退回来,因为在长期的支持,最终将标准化的其余部分的存在。

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

但是浏览器可能不支持它,或者出于安全原因客户端已经禁用了它。无论如何,人们不应该长期依赖这种“黑客行为”,因为它很可能在未来得到修补(见卡伦 · 弗拉菲 · 詹宁斯的回答)。

下面的 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>

Chrome76 +

去年,我使用 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

Now supported in 内部 IP!

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

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

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

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