Electron与Node.js环境下怎么获取本机MAC地址
详细讲解在Node.js和Electron环境下获取本机MAC地址的具体方法,涵盖os.networkInterfaces过滤、系统命令与第三方库调用、跨进程安全IPC通信,以及虚拟网卡排查与多平台容错实践。
Electron 环境下的双进程架构与安全获取路径
Electron 结合了 Chromium 的渲染引擎和 Node.js 运行时。现代 Electron(自 12.x 起默认开启 contextIsolation: true,且 nodeIntegration: false)对渲染进程实施严格的安全隔离。在前端页面代码中直接执行 require('os') 会直接抛出未定义异常。
因此,获取 MAC 地址必须遵循“主进程处理系统底层交互、预加载脚本暴露受控白名单 API、渲染进程发起异步调用”的通信规范。
1. 主进程(Main Process)注册 IPC 监听
在主进程文件(例如 main.js)中,使用 ipcMain.handle 注册处理句柄。主进程拥有完整的 Node.js 原生权限,可以安全引入 os 模块并执行网卡过滤逻辑。
// main.js (主进程)
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const { getPhysicalMacAddress } = require('./mac-util');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 启用上下文隔离
nodeIntegration: false // 禁用前端节点集成
}
});
win.loadFile('index.html');
}
// 注册获取 MAC 地址的双向通信处理方法
ipcMain.handle('system:get-mac-address', async () => {
return getPhysicalMacAddress();
});
app.whenReady().then(createWindow);
2. 预加载脚本(preload.js)建立安全桥接
预加载脚本充当渲染进程与主进程之间的受控通道。通过 contextBridge.exposeInMainWorld 仅将安全的异步函数暴露到前端全局 window 对象上,坚决避免把整个 ipcRenderer 对象直接挂载到前端。
// preload.js (预加载脚本)
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('systemAPI', {
getMacAddress: () => ipcRenderer.invoke('system:get-mac-address')
});
3. 渲染进程(Renderer Process)异步获取结果
前端页面(如 Vue、React 或原生 JavaScript)通过 window.systemAPI.getMacAddress() 即可直接得到规范化的 MAC 地址字符串。
// renderer.js (渲染进程/前端业务逻辑)
async function loadDeviceInfo() {
try {
const mac = await window.systemAPI.getMacAddress();
console.log('本机真实物理 MAC 地址:', mac);
document.getElementById('mac-display').innerText = mac;
} catch (err) {
console.error('获取 MAC 地址失败:', err);
}
}
loadDeviceInfo();
Node.js 原生方案:os.networkInterfaces() 的解析与多级过滤
在 Node.js 中,os.networkInterfaces() 会返回当前宿主机所有已分配网络接口的哈希字典,键为网卡名称(如 en0、eth0 或 WLAN),值为包含该接口地址详情的数组。
然而,直接遍历取第一个非空结果往往会得到错误地址。主要原因在于现代操作系统中充斥着各类非物理网络接口:
- 本地回环接口(Loopback):其
internal属性为true,MAC 地址通常标记为全零00:00:00:00:00:00。 - 虚拟机与容器适配器:VMware(如
VMnet1、VMnet8)、VirtualBox(vboxnet)、WSL 虚拟交换机(vEthernet)以及 Docker(docker0)会注册虚拟 MAC,混淆真实物理机标识。 - VPN 与隧道设备:TUN/TAP 设备通常是纯软件封装的临时网络隧道。
稳健的物理 MAC 地址提取函数
以下为生产可用的多级过滤实现代码,放置于工具模块 mac-util.js 中:
// mac-util.js
const os = require('os');
/**
* 获取本机首选真实物理网卡的 MAC 地址
* @returns {string|null} 格式为 "aa:bb:cc:dd:ee:ff" 的物理网卡地址
*/
function getPhysicalMacAddress() {
const interfaces = os.networkInterfaces();
const macRegex = /^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i;
// 虚拟网卡常见命名模式正则
const virtualNamePattern = /(loopback|vmnet|vbox|docker|vethernet|tun|tap|virbr|dummy)/i;
const candidates = [];
for (const [name, ifaceList] of Object.entries(interfaces)) {
// 过滤常见的虚拟网卡接口名称
if (virtualNamePattern.test(name)) {
continue;
}
if (!Array.isArray(ifaceList)) continue;
for (const item of ifaceList) {
// 排除内部回环和缺少 MAC 的项
if (item.internal || !item.mac) {
continue;
}
const normalizedMac = item.mac.toLowerCase().replace(/-/g, ':');
// 排除全零无效 MAC 与广播/多播默认地址
if (
normalizedMac === '00:00:00:00:00:00' ||
normalizedMac === 'ff:ff:ff:ff:ff:ff' ||
!macRegex.test(normalizedMac)
) {
continue;
}
// 赋予候选权重:有线物理网卡(en/eth)与无线(wlan/wi-fi)优先
let priority = 10;
if (/^(en|eth)/i.test(name)) {
priority = 30; // 有线以太网最高优先级
} else if (/^(wlan|wi-fi|wl)/i.test(name)) {
priority = 20; // 常见无线网卡
}
candidates.push({
name,
mac: normalizedMac,
family: item.family,
priority
});
}
}
if (candidates.length === 0) {
return null;
}
// 优先返回权重最高且为 IPv4 激活态的物理适配器
candidates.sort((a, b) => b.priority - a.priority);
return candidates[0].mac;
}
module.exports = { getPhysicalMacAddress };
系统命令行与第三方库方案:精准匹配活动网卡
在某些复杂的企业内外网隔离网络环境,或主机同时插载多块物理网卡时,os.networkInterfaces() 仅能列出所有绑定 IP 的网卡,但无法感知该网卡在硬件层面上是否插有物理网线,也无法直接获知哪一块网卡承担着当前系统的“默认网关路由(Default Gateway Route)”。
针对此类进阶需求,可引入成熟的第三方封装库(如 getmac 或 node-macaddress),或者在主进程中使用 child_process.exec 执行操作系统底层工具命令。
1. 使用 getmac 库简化多平台调用
getmac 库在内部根据不同操作系统调用对应的系统命令(Windows 上调用 getmac.exe,macOS 调用 ifconfig / networksetup,Linux 上扫描 /sys/class/net/ 节点),能够较为准确地定位当前活动网卡。
// 安装:npm install getmac
const { getMAC, isMAC } = require('getmac');
try {
const mac = getMAC();
if (isMAC(mac)) {
console.log('通过系统命令定位的当前活动 MAC:', mac);
}
} catch (err) {
console.error('执行底层网卡查询失败:', err);
}
2. 命令行方案的局限与排错提醒
系统命令行输出高度依赖操作系统的系统语言(Language Pack)。例如在中文版 Windows 运行
ipconfig /all 或 getmac /v,输出字段为“物理地址”或“媒体已断开”,若依赖针对英文系统(如“Physical Address”)编写的正则表达式,会因字符不匹配直接导致解析失败。因此在生产中建议优先使用原生 os.networkInterfaces() 配合过滤算法,仅在必要时采用规范的结构化输出命令(如 getmac /fo csv /nh)作为补充兜底。
生产环境避坑指南与跨平台容错实践
在桌面端生产环境中,MAC 地址往往被用于生成软件试用期绑定、设备激活或日志排查标识。在实际落地中,需要特别关注以下三个核心问题。
1. 警惕 Wi-Fi MAC 地址随机化(私密 Wi-Fi 地址)
现代操作系统(Windows 10/11、macOS Sequoia/Sonoma)为了保护用户隐私,针对无线 Wi-Fi 默认引入了“随机 MAC 地址”特性。当用户连接不同无线网络或网络配置更新时,无线网卡的 MAC 会发生变动。
应对建议:若业务要求设备标识保持长期不变,应在权重排序中优先选取固定物理有线以太网(Ethernet);若仅有无线网卡,在应用首次安装或用户首次授权登录时,应将取得的 MAC 结合设备主板 UUID 共同生成复合哈希,并在客户端本地持久化存储(如通过加密的配置文件保存),避免频繁读取导致标识漂移。
2. 网卡拔插引起的标识漂移
笔记本电脑用户常在工位(插网线)与移动办公(连 Wi-Fi)之间切换。如果直接取当前处于连接状态的网卡,会导致设备指纹随网络连接方式切换而变化。
应对建议:在主进程中建立确定性的全量物理网卡扫描列表,按照固定的算法对所有符合物理特征的 MAC 地址进行字母升序排序,拼接后取摘要:
const crypto = require('crypto');
// 生成稳定防漂移的多物理网卡联合哈希
function generateStableHardwareHash(macList) {
const sorted = [...macList].sort();
return crypto.createHash('sha256').update(sorted.join(';')).digest('hex');
}
3. 方案选型综合评估
| 对比维度 | Node.js 原生 API (os.networkInterfaces) | 第三方系统命令 (getmac / 系统命令) | 生产推荐联合方案 |
|---|---|---|---|
| 执行耗时 | < 1 ms(内存级读取) | 50 ~ 150 ms(派生子进程) | 首次初始化 < 2 ms,后续读取缓存 0 ms |
| 外部依赖 | 零依赖(Node.js 标准库) | 依赖外部 npm 包及系统二进制 | 内置原生过滤为主,纯代码封装 |
| 系统语言影响 | 无任何影响(键值标准化) | 存在本地化输出匹配失效风险 | 不受操作系统语言包干扰 |
| 稳定性保证 | 高(需自研正则排除虚拟卡) | 较高(需处理子进程异常) | 极高(主进程过滤 + 首次持久化落盘) |
在 NestJS 中使用管道验证数据与守卫控制访问权限
2026-11-05 12:43:41
Node.js后端生成高强度随机字符串的方法
2026-11-04 16:29:54
Node.js环境中怎么使用axios或http模块发送GET请求
2026-11-03 21:57:15
Node.js环境下怎么实现JS反混淆自动化?常用库与CLI工具推荐
2026-11-03 21:15:54
解决JS逆向常见报错:缺少DOM环境导致代码运行失败
2026-11-03 20:41:59








