前端js获取内网ip
奇怪,万里汇认证页面,居然可以识别出同网络下、同浏览器的两个手机,应该是前端识别到手机的内网IP或者MAC,之前也了解过前端JS获取内网IP或MAC。在github上有个库 webrtc-ip DEMO
getIp.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/gh/joeymalvinni/webrtc-ip/dist/production.min.js"></script> <title>getIp</title> </head> <body> <span>本机ip为:</span> <span id="ip"></span> <script> getIPs().then(data => { var ips = data.join('\n') document.getElementById('ip').innerHTML = ips }) </script> </body> </html>
production.min.js
/****/ function is_ipv4(d){return regex_v4.test(d)}function is_ipv6(d){return regex_v6.test(d)}var simpleIPRegex=/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g;let regex_v4=/((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/,regex_v6=/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/,ip_regex_array=[regex_v6,regex_v4];function peer(d){var e,t=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection;function n(){(e=new t({iceServers:[{urls:"stun:stun.l.google.com:19302"}]})).onicecandidate=(d=>f(d)),e.createDataChannel("fake_data_channel")}function a(){return e.createOffer().then(d=>e.setLocalDescription(d))}function f(e){e&&e.candidate&&e.candidate.candidate&&d(e&&e.candidate&&e.candidate.candidate)}return{start:function(){n(),a()},stop:function(){if(e)try{e.close()}finally{e.onicecandidate=(()=>{}),e=null}},createConnection:n,createStunRequest:a,handleCandidates:f}}function publicIPs(d){if(d&&d<100)throw new Error("Custom timeout cannot be under 100 milliseconds.");var e=[],t=peer(function(d){var t=[];for(let e of ip_regex_array){let n=[],a=e.exec(d);if(a){for(let d=0;d<a.length;d++)(is_ipv4(a[d])||is_ipv6(a[d]))&&n.push(a[d]);t.push(n)}}!function(d){e.includes(d)||e.push(n(d.flat(1/0)))}(t.flat(1/0))});function n(d){return Array.from(new Set(d))}return new Promise(function(a,f){t.start(),setTimeout(()=>{e&&e!==[]?a(n(e.flat(1/0))):f("No IP addresses were found."),t.stop()},d||500)})}function getIPTypes(d){return new Promise(function(e,t){let n=[];publicIPs(d).then(d=>{d.forEach(d=>{d.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)?n.push({ip:d,type:"private",IPv4:!0}):d.match(/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/)?n.push({ip:d,type:"IPv6",IPv4:!1}):n.push({ip:d,type:"public",IPv4:!0})}),e(n)}).catch(t)})}function getIPv4(d){return getIPTypes(d).then(d=>{const e=d.filter(d=>d.IPv4);for(let d=0;d<e.length;d++)e[d]=e[d].ip;return e||""})}function getIPv6(d){return getIPTypes(d).then(d=>{const e=d.filter(d=>"IPv6"===d.type);for(let d=0;d<e.length;d++)e[d]=e[d].ip;return e?e.ip:""})}function getIPs(d){return Object.assign(publicIPs(d),{types:getIPTypes,public:publicIPs,IPv4:getIPv4,IPv6:getIPv6})}
开发版js
/* * This file is the entire script combined in working order. * Copyright 2021 © Joey Malvinni * License: MIT */ // [---------------------------------------------------------------------------] // File: ip_validator.js /* * This module validates the two types of IP addresses. * Copyright 2021 © Joey Malvinni * License: MIT */ // The function that checks if the given IPv4 address is valid. function is_ipv4(ip){ return regex_v4.test(ip); }; // Checks if the IPv6 address is valid. function is_ipv6(ip){ return regex_v6.test(ip); }; // Simple IP regex that works on both IPv6 and IPv4 var simpleIPRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g; // IPv4 regex used to determine whether an IP is IPv4 or not. let regex_v4 = /((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/; // The IPv6 regex used when determining an IPv6 address. let regex_v6 = /((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/; // Exporting the two regexes in an array to be used in the main detector. let ip_regex_array = [regex_v6, regex_v4] // [---------------------------------------------------------------------------] // File: peer_conn.js /* * This module provides the main WebRTC functions that return IP addresses from the STUN request. * Copyright 2021 © Joey Malvinni * License: MIT */ function peer(callback){ // Creating the peer connection. var WebRTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; // Initializing the connection. var createdConnection; // Main start function. function start(){ // Creating the actual connection. createConnection() // Log the STUN request. createStunRequest() }; // Stop function to reset the connection. function stop(){ // Checking if the connection has been created or not. if (createdConnection) { // Attempt to close it, if RTCPeerConnection.close() is not supported, remove the event listeners. try { createdConnection.close(); } finally { createdConnection.onicecandidate = () => {}; createdConnection = null; }; }; }; // Function that makes the connection request to Google's STUN server function createConnection(){ let iceServers = [{ urls: 'stun:stun.l.google.com:19302' }]; // Creating the connection with the STUN server. createdConnection = new WebRTCPeerConnection({ iceServers }); // Handling the ICE candidate event. createdConnection.onicecandidate = (data) => handleCandidates(data); // Creation of the fake data channel. createdConnection.createDataChannel('fake_data_channel'); }; // Function that creates the STUN request offer needed to get the IPs. function createStunRequest(){ // Create the offer that exposes the IP addresses. return createdConnection.createOffer().then(sdp => createdConnection.setLocalDescription(sdp)); }; // Handling the onIceCandidate event. function handleCandidates(ice){ // Checking if the ICE candidate lines given are valid. if (ice && ice.candidate && ice.candidate.candidate) { // Returning the IPs to the main function. callback(ice && ice.candidate && ice.candidate.candidate); }; }; // Returning the main functions needed. return { start, stop, createConnection, createStunRequest, handleCandidates }; }; // [---------------------------------------------------------------------------] // File: public_ip.js /* * This module provides the worker functions that return the public IP addresses. * Copyright 2021 © Joey Malvinni * License: MIT */ function publicIPs(timer){ // Timing validation. if(timer) if(timer < 100) throw new Error('Custom timeout cannot be under 100 milliseconds.'); // IPs is the final array of all valid IPs found. var IPs = []; // Creating the peer connection request while handling the callback event. var peerConn = peer(handleIceCandidates); function getIps(){ // Returning a promise. return new Promise(function(resolve, reject) { // Starting the peer connection. peerConn.start(); // Setting the timer. setTimeout(() => { // Checking if the IP array exists. if(!IPs || IPs === []){ // Rejecting the error reject('No IP addresses were found.') } else { // Return the unique IP addresses in an array. resolve(unique(IPs.flat(Infinity))) }; // reset the peer connection. reset(); // Set the Timeout to the custom timer, default to 500 milliseconds. }, timer || 500); }); }; function handleIceCandidates(ip){ var array = []; // Looping over the two regexs for IPv6 and IPv4 for(let regex of ip_regex_array){ let arr = []; // Lutting all of the strings that match either IP format in an array let possible_ips_array = regex.exec(ip) if(possible_ips_array){ // Looping over that array for(let i = 0; i < possible_ips_array.length; i++){ // Checking if the "IP" is valid if(is_ipv4(possible_ips_array[i]) || is_ipv6(possible_ips_array[i])){ arr.push(possible_ips_array[i]) }; }; array.push(arr); }; }; // Final function that does more checks to determine the array's validity, // Also flattens the array to remove extraneous sub-arrays. push(array.flat(Infinity)) }; function push(ip){ // Checks if the IP addresses givin are already in the array. if(!IPs.includes(ip)){ IPs.push(unique(ip.flat(Infinity))); }; }; function reset(){ // Stops the peer connection to the STUN server. peerConn.stop() }; // Use this to only return unique IP addresses. function unique(a) { return Array.from(new Set(a)); }; return getIps(); }; // [---------------------------------------------------------------------------] // File: index.js /* * This module combines all of the worker modules into the main functions that get exported. * Copyright 2021 © Joey Malvinni * License: MIT */ // Categorizes the IPs by IP, type, and IPv4. function getIPTypes(timer){ // Returning the result as a promise. return new Promise(function(resolve, reject) { // Final array let finalIpArray = [] // Getting the raw IPs in an array. publicIPs(timer).then((ips)=>{ // Looping over each IP. ips.forEach(ip => { if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) { // The IP is private. finalIpArray.push({ ip: ip, type: 'private', IPv4: true }) } else if (ip.match(/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))/)) { // The IP is an IPv6 address. finalIpArray.push({ ip: ip, type: 'IPv6', IPv4: false }) } else { // Assume the IP is public. finalIpArray.push({ ip: ip, type: 'public', IPv4: true }) } }) // Resolving the promise. resolve(finalIpArray) }).catch(reject) }) } // Filters out IPv4 addresses. function getIPv4(timer) { return getIPTypes(timer).then(ips => { // Filters the IP by IPv4. const ip = ips.filter(ip => ip.IPv4); // Loops over each object and extracts the IP. for(let i = 0; i < ip.length; i++){ ip[i] = ip[i].ip } // Returns undefined if the array is empty. return ip ? ip : ''; }); } // Filters out IPv6 addresses. function getIPv6(timer) { // Getting the IPs by type. return getIPTypes(timer).then(ips => { // Filtering the IPs by IPv6. const ip = ips.filter(ip => ip.type === 'IPv6'); // Extracting the IPs for(let i = 0; i < ip.length; i++){ // Removing all other data from the object. ip[i] = ip[i].ip } // Returning the IP or undefined. return ip ? ip.ip : ''; }); } // Returns all of the functions in an object, default to getting all of the IPs without any filtering applied. function getIPs(timer){ return Object.assign( publicIPs(timer), { types: getIPTypes, public: publicIPs, IPv4: getIPv4, IPv6: getIPv6, } ) };