本文翻译自 Discover WebRTC: Obtain User IP Addresses in the Browser,作者:Zack, 略有删改。
如果需要在程序中获取当前用户的IP,通常手段都是需要使用服务器。但现在借助WebRTC
的强大功能,我们可以直接在浏览器客户端获取用户IP。
什么是WebRTC
WebRTC
是一个开源项目,通过简单的API为Web浏览器和移动的应用程序提供实时通信功能。WebRTC
支持浏览器之间的音频、视频和数据流,允许开发人员构建视频聊天、文件传输和屏幕共享等应用程序,而无需依赖第三方插件或扩展。
WebRTC
的关键组件
WebRTC
由几个关键组件组成,这些组件共同提供实时通信:
getUserMedia
:允许访问用户的网络摄像头和麦克风。RTCPeerConnection
:管理建立和维护对等点之间连接的整个过程。RTCDataChannel
:支持对等点之间的双向数据传输,允许低延迟通信。
WebRTC
协议
WebRTC
依赖于几种协议来建立和维护对等体之间的连接:- ICE(Interactive Connectivity Establishment):通过NAT(Network Address Translators)和防火墙连接对等体的框架,ICE使用STUN和TURN服务器来发现和中继网络信息。
- STUN(Session Traffic Utilities for NAT):一种允许客户端发现其公有IP地址和所处NAT类型的协议。
- TURN(使用NAT周围的中继进行传输):为由于NAT限制或防火墙而无法建立直接连接的对等体提供中继服务器的协议。
使用WebRTC
获取用户IP地址
需要注意的是,使用WebRTC
获取用户IP地址可能会引发隐私问题。在收集任何信息(包括IP地址)之前,必须通知您的用户并征得他们的同意。
要使用WebRTC
获取用户的IP地址,您可以利用ICE流程,该流程在对等点之间交换网络信息。在ICE过程中,浏览器收集本地IP地址并生成包含此信息的ICE候选。
创建RTCPeerConnection
开始获取用户IP地址的过程,请使用ICE服务器创建一个RTCPeerConnection
:
js
const iceServers = [
{
urls: "stun:stun.l.google.com:19302",
},
];
const peerConnection = new RTCPeerConnection({ iceServers });
这个连接对象表示两个对等体之间的连接,负责管理数据交换。
"stun:stun.l.google.com:19302
"是由Google提供的公共STUN服务器,它是一个高度可靠的免费公共STUN服务器。如果需要,您可以选择使用自己的STUN服务器。
创建数据通道
对于某些浏览器(如Chrome),创建数据通道是触发ICE进程所必需的。您可以使用createDataChannel
方法创建数据通道:
js
peerConnection.createDataChannel("");
处理 ICE 候选项
监听onicecandidate
事件以在生成ICE候选时收集IP地址:
js
peerConnection.onicecandidate = (event) => {
console.log("RTC Peer Connection Ice Event:", event);
};
触发ICE
调用createOffer
和setLocalDescription
方法触发ICE进程:
js
peerConnection
.createOffer()
.then((offer) => peerConnection.setLocalDescription(offer))
.catch((error) => console.error("Error creating offer:", error));
提取IP地址
一旦我们收集了ICE候选项,我们就可以从中提取用户的IP地址。每个ICE候选包含关于潜在网络路径的信息,包括IP地址和端口。
所以在onicecandidate
中,我们可以创建一个正则表达式来提取IP地址,代码如下:
js
const ipv4Regex =
/\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/;
const ipv6Regex = /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i;
const ipSet = new Set();
const onicecandidate = (ice: RTCPeerConnectionIceEvent) => {
const candidate = ice?.candidate?.candidate;
if (candidate) {
for (const regex of [ipv4Regex, ipv6Regex]) {
const [ip] = candidate.match(regex) ?? [];
if (ip) {
ipSet.add(ip);
}
}
}
};
完整代码
js
const ipv4Regex =
/\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/;
const ipv6Regex = /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i;
// prettier-ignore
// @ts-expect-error
globalThis.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection
const publicIPs = (timeout = 1000) => {
const ipSet = new Set();
const onicecandidate = (ice: RTCPeerConnectionIceEvent) => {
const candidate = ice?.candidate?.candidate;
if (candidate) {
for (const regex of [ipv4Regex, ipv6Regex]) {
const [ip] = candidate.match(regex) ?? [];
if (ip) {
ipSet.add(ip);
}
}
}
};
return new Promise((resolve, reject) => {
const conn = new globalThis.RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302',
},
],
});
conn.addEventListener('icecandidate', onicecandidate);
conn.createDataChannel('');
conn.createOffer().then((offer) => conn.setLocalDescription(offer), reject);
setTimeout(() => {
try {
conn.removeEventListener('icecandidate', onicecandidate);
conn.close();
} catch {
// ignore
}
resolve([...ipSet]);
}, timeout);
});
};
publicIPs().then(console.log);
在完整代码中,我添加了RTCPeerConnection
的全局兼容性代码。考虑到一些网络因素,您可能需要设置超时,因此我在这里设置了默认超时1s。
限制和隐私问题
虽然WebRTC
方法在获取IP地址方面是有效的,但它有一定的局限性,并引起了隐私问题:
网络限制
某些网络和浏览器可能会出于隐私和安全原因限制WebRTC
功能。在这种情况下,该方法可能无法按预期工作。
隐私问题
使用WebRTC
获取IP地址可以被视为隐私问题。在收集IP地址之前,请务必告知用户您的意图并征得他们的同意。
替代方法
除了WebRTC
,您还可以使用其他方法来获取用户的IP地址。这些方法也可能存在局限性和隐私问题:
HTTP请求
如果您可以控制服务器端应用程序,则可以从HTTP请求头中获取用户的IP地址。大多数Web服务器框架都提供了访问这些信息的简单方法。但是如果用户位于代理或VPN后面,则此方法可能无法正常工作。
第三方api
您可以使用第三方IP地址查找服务(如ipify
或ip-api
)来获取用户的IP地址。这些服务通常提供一个简单的RESTful API
来获取JSON
格式的用户IP地址。请注意,依赖第三方服务可能会导致隐私问题,如果提供商遇到停机或速率限制,可能会导致服务中断。
结论
WebRTC
是一种强大的技术,可以实现Web浏览器之间的实时通信。它还可以用于获取用户IP地址,这在某些情况下可能很有用。但是考虑与此方法相关的限制和隐私问题至关重要。在收集任何个人信息(包括IP地址)之前,请始终告知您的用户您的意图并征得他们的同意。
看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~
专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)