Electron IPC 与节点集成

IT技术 javascript reactjs webpack electron
2021-03-01 19:25:55

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

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

把我的问题交给一些同事后,有人建议我应该在我的main.js文件中更改

webPreferences: {
    nodeIntegration: false,
}

webPreferences: {
    nodeIntegration: true,
}

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

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

我的问题如下。

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

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

1个回答
  1. 是否可以在不启用 nodeIntegration 的情况下使用 ipcRenderer?

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

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

可以使用preload如下所示脚本。但是,不被认为是安全的大多数现有文档没有显示最佳安全实践。

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

// 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 时也会变得复杂,因为输出文件可能是不同的路径。

  1. 如果不是,我用什么?

编辑 正如@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,
});
那么我们应该为每个窗口创建一个唯一的预加载吗?
2021-04-28 19:25:55
请注意,此配置是为了最大程度的安全,假设您的应用程序可能会打开外部 URL。如果您小心锁定外部站点的使用,您可以使用具有一定安全性的节点集成。
2021-04-30 19:25:55
您实际上无法使用此系统使用“require”,因为它是唯一通过静默序列化和反序列化的消息进行的通信。这意味着您不能通过身份传递对象(仅传递数据的副本),并且任何特殊类都将丢失。本质上它就像在做 JSON.parse(JSON.serialize(...))。您只能传递数据。您基本上需要在主进程中实现任何类似节点的逻辑,并使用消息向渲染器进程发送/接收纯数据
2021-05-01 19:25:55
据我所知,您最好使用单个预加载并在那里基本上创建一种 API。同样,如果您不加载/允许外部站点,您可能可以跳过某些安全性。
2021-05-04 19:25:55
是否可以对渲染器进程中的 require() module使用类似的方法?@卢克
2021-05-08 19:25:55