Electron IPC 和节点集成

Posted

技术标签:

【中文标题】Electron IPC 和节点集成【英文标题】:Electron IPC and nodeIntegration 【发布时间】:2019-02-13 15:21:17 【问题描述】:

因此,我遵循了许多指南来设置 Webpack、Electron 和 React 以制作桌面应用程序。完成设置后,我开始工作,并了解到我需要来自 main 和 renderer 的 IPC 机制才能进行通信。

import ipcRenderer from "electron"; 将此添加到我的 renderer.js 文件会导致错误 Uncaught ReferenceError: require is not defined

在向一些同事提出我的问题后,有人建议我在我的 main.js 文件中进行更改

webPreferences: 
    nodeIntegration: false,

webPreferences: 
    nodeIntegration: true,

我在谷歌上读到的每一个地方都非常清楚地表明,如果安全是你关心的事情,那么这不是你应该做的事情。但是,我能遇到的有关电子 ipc 的所有资源都使用了 ipcRenderer。

现在,互联网上的每个示例是否都存在巨大的安全漏洞,还是我在这里遗漏了一些关键部分?

我的问题如下。

    是否可以在不启用 nodeIntegration 的情况下使用 ipcRenderer? 如果是,我该怎么做,为什么这么多资源会排除这些信息? 如果不是,我用什么?

如果我问错了问题,或者我遗漏了什么,或者我提出这个问题的方式有任何其他明显问题,请告诉我,否则提前致谢。

【问题讨论】:

nodeIntegration 启用/禁用 NodeJS 的使用,由于 Electron 是一个 NodeJS 模块,没有 NodeJS 就无法使用它。所以,不,如果你想使用 Electron 的 ipcRenderer,你必须启用 NodeJS。 我同意。将nodeIntergation 设置为false 时,mainrenderer 进程之间根本无法通信。我真的想知道当电子设置为false 时,电子在现实世界中的用途是什么,现在他们将其设为默认值。 @jayarjo 我不明白,因为根据电子文档nodeIntegration 默认设置为false,所以一定不需要? nodeIntegration被禁用时,你仍然可以通过其他方式与主线程通信。例如,您可以建立WebSocket 连接或标准 HTTP 方法(例如,GET / POST 后台 JSON)。以下 Luke H 的答案为您的所有 3 个问题提供了详细的解释和解决方案,我建议将其标记为已接受的答案。 在Q&A 中,我发布了一个示例,说明如何通过预加载脚本在主进程和渲染器进程之间设置基于 IPC 的通信。希望对您有所帮助。 【参考方案1】:
    是否可以在不启用 nodeIntegration 的情况下使用 ipcRenderer?

这是可能的,但很繁琐。可以使用preload 脚本来完成。

    如果是,我该怎么做,为什么这么多资源会排除这些信息?

可以使用preload 脚本,如下所示。但是,这是not considered secure。大多数现有文档都没有展示最佳安全实践。

后面会给出一个更安全的例子。

// preload.js
const electron = require('electron');

process.once('loaded', () => 
  global.ipcRenderer = electron.ipcRenderer;
);
// main.js
const app, BrowserWindow = require('electron');

app.on('ready', () => 
  // Create the browser window.
  win = new BrowserWindow(
      backgroundColor: '#fff', // always set a bg color to enable font antialiasing!
      webPreferences: 
        preload: path.join(__dirname, './preload.js'),
        nodeIntegration: false,
        enableRemoteModule: false,
        // contextIsolation: true,
        // nativeWindowOpen: true,
        // sandbox: true,
      
  );
  win.loadURL(`file://$path.join(__dirname, 'index.html')`);

注意预加载脚本的路径必须是绝对路径,这也可以 使用 webpack/babel 时会变得复杂,因为输出文件可能是不同的路径。

    如果不是,我用什么?

编辑 正如@Yannic 指出的那样,Electron 现在支持另一个选项,称为contextBridge。这个新选项可以更简单地解决问题。有关contextBridge 的信息,请查看电子文档:https://www.electronjs.org/docs/tutorial/context-isolation

但是,即使使用 contextBridge,您也不应该尝试公开整个电子 API,而只是为您的应用设计的有限 API

如前所述,尽管可以使用如上所示的 ipcRenderer,但当前的电子安全建议也建议启用 contextIsolation。这将使上述方法无法使用,因为您无法再将数据添加到全局范围。

AFAIK 最安全的建议是改用addEventListenerpostMessage,并使用预加载脚本作为渲染器和主脚本之间的桥梁。

// preload.js
const  ipcRenderer  = require('electron');

process.once('loaded', () => 
  window.addEventListener('message', event => 
    // do something with custom event
    const message = event.data;

    if (message.myTypeField === 'my-custom-message') 
      ipcRenderer.send('custom-message', message);
    
  );
);
// main.js
const app, ipcMain, BrowserWindow = require('electron');

app.on('ready', () => 
  ipcMain.on('custom-message', (event, message) => 
    console.log('got an IPC message', e, message);
  );

  // Create the browser window.
  win = new BrowserWindow(
      backgroundColor: '#fff', // always set a bg color to enable font antialiasing!
      webPreferences: 
        preload: path.join(__dirname, './preload.js'),
        nodeIntegration: false,
        enableRemoteModule: false,
        contextIsolation: true,
        sandbox: true,
        // nativeWindowOpen: true,
      
  );
  win.loadURL(`file://$path.join(__dirname, 'index.html')`);
// renderer.js
window.postMessage(
  myTypeField: 'my-custom-message',
  someData: 123,
);

【讨论】:

请注意,此配置是为了最大限度地提高安全性,假设您的应用可能会打开外部 URL。如果您仔细锁定外部站点的使用,您可以使用具有一定安全性的节点集成。 所以我们应该为每个窗口创建一个唯一的预加载? 据我所知,您最好使用单个预加载并基本上在那里创建一种 API。同样,如果您不加载/允许外部站点,您可能可以跳过其中的一些安全性。 可以在渲染进程中使用类似的方法来 require() 模块吗? @LukeH 你实际上不能在这个系统中使用'require',作为唯一通过静默序列化和反序列化的消息进行的通信。这意味着您不能通过身份传递对象(仅传递数据的副本),并且任何特殊类都将丢失。本质上它就像在做 JSON.parse(JSON.serialize(...))。您只能传递数据。您基本上需要在主进程中实现任何类似节点的逻辑,并使用消息向渲染器进程发送/接收纯数据

以上是关于Electron IPC 和节点集成的主要内容,如果未能解决你的问题,请参考以下文章

使用 ipc 渲染器从 mongodb 和 electron js 获取数据

Electron Renderer 进程:我啥时候应该清理 IPC 监听器

Electron - IPC - 在窗口之间发送数据

如何使Electron进行IPC 通信?

Electron(基于Vue)中使用IPC

Electron(基于Vue)中使用IPC