问题描述
因此,我遵循了许多指南来设置 Webpack、Electron 和 React 以制作桌面应用程序.完成设置后,我开始工作,并了解到我需要来自 main 和 renderer 的 IPC 机制才能进行通信.
从electron"导入{ipcRenderer};
将此添加到我的 renderer.js 文件会导致错误 Uncaught ReferenceError: require is not defined
.
在向一些同事提出我的问题后,有人建议我在我的 main.js 文件中进行更改
webPreferences: {节点集成:假,}
到
webPreferences: {节点集成:真,}
我在谷歌上读到的所有地方都非常清楚地表明,如果安全是你关心的事情,那么这不是你应该做的事情.但是,我能遇到的有关电子 ipc 的所有资源都使用了 ipcRenderer.
现在,互联网上的每个示例是否都存在巨大的安全漏洞,还是我在这里遗漏了一些关键部分?
我的问题如下.
- 是否可以在不启用 nodeIntegration 的情况下使用 ipcRenderer?
- 如果是,我该怎么做,为什么这么多资源会排除这些信息?
- 如果不是,我用什么?
如果我问错了问题,或者我遗漏了什么,或者我提出这个问题的方式有任何其他明显问题,请告诉我,否则提前致谢.
- 是否可以在不启用 nodeIntegration 的情况下使用 ipcRenderer?
这是可能的,但很繁琐.这可以通过使用
<块引用>preload
脚本来完成.
- 如果是,我该怎么做,为什么这么多资源会排除这些信息?
可以使用
preload
脚本,如下所示.但是,这是 不安全.大多数现有文档都没有展示最佳安全实践.后面会给出一个更安全的例子.
//preload.js常量电子 = 要求('电子');process.once('加载', () => {global.ipcRenderer = 电子.ipcRenderer;});
//main.jsconst {app, BrowserWindow} = 要求('电子');app.on('准备好了', () => {//创建浏览器窗口.赢=新浏览器窗口({backgroundColor: '#fff',//始终设置背景颜色以启用字体抗锯齿!网络偏好:{预加载:path.join(__dirname, './preload.js'),节点集成:假,启用远程模块:假,//contextIsolation: true,//nativeWindowOpen: 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 是使用 addEventListener
和 postMessage
代替,并使用预加载脚本作为渲染器和主脚本之间的桥梁.
//preload.js常量 { ipcRenderer } = 要求('电子');process.once('加载', () => {window.addEventListener('消息', 事件 => {//用自定义事件做一些事情常量消息 = 事件数据;if (message.myTypeField === 'my-custom-message') {ipcRenderer.send('自定义消息', message);}});});
//main.jsconst {app, ipcMain, BrowserWindow} = require('electron');app.on('准备好了', () => {ipcMain.on('custom-message', (event, message) => {console.log('得到一个IPC消息', e, message);});//创建浏览器窗口.赢=新浏览器窗口({backgroundColor: '#fff',//始终设置背景颜色以启用字体抗锯齿!网络偏好:{预加载:path.join(__dirname, './preload.js'),节点集成:假,启用远程模块:假,上下文隔离:真,沙盒:真,//nativeWindowOpen: true,}});win.loadURL(`file://${path.join(__dirname, 'index.html')}`);
//renderer.jswindow.postMessage({myTypeField: '我的自定义消息',一些数据:123,});
So, I've followed a number of guides to set up Webpack, Electron, and React to make a desktop application. After finishing the setup, I got to work, and learned that I needed to require an IPC mechanism from the main and renderer in order to communicate.
import {ipcRenderer} from "electron";
Adding this to my renderer.js file causes the error Uncaught ReferenceError: require is not defined
.
After taking my problem to some colleagues, it was suggested that in my main.js file I should change
webPreferences: {
nodeIntegration: false,
}
to
webPreferences: {
nodeIntegration: true,
}
Everywhere I've read on google has said very clearly that if safety is something you care about, this is not something you should do. However, every resource I've been able to come across for electron ipc has used the ipcRenderer.
Now, does every example on the internet have huge security flaws, or am I missing some key part here?
My questions are as follows.
- Is it possible to use ipcRenderer without enabling nodeIntegration?
- If it is, how do I do it, and why would so many resources exclude this information?
- If it is not, what do I use?
If I'm asking the wrong question, or I missed something, or there are any other clear problems with the way I've asked this question please let me know, otherwise thanks in advance.
- Is it possible to use ipcRenderer without enabling nodeIntegration?
It is possible, but fiddly. It can be done by using a preload
script.
- If it is, how do I do it, and why would so many resources exclude this information?
It is possible, using the preload
script as indicated below. However, this is not considered secure. Most of the existing documentation does not show best security practices.
A more secure example is given afterwards.
// 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')}`);
NOTE That the path to the preload script must be absolute and this can also get complicated when using webpack/babel, as the output file may be a different path.
- If it is not, what do I use?
Edit
As @Yannic pointed out, there is now another option supported by Electron, called contextBridge
. This new option may solve the problem more simply. For info on contextBridge
, check the electron docs: https://www.electronjs.org/docs/tutorial/context-isolation
However, even with contextBridge
you should not be try to expose entire electron APIs, just a limited API you have designed for your app
As mentioned, although it is possible to use ipcRenderer as shown above, the current electron security recommendations recommend also enabling contextIsolation
. This will make the above approach unusable as you can no longer add data to the global scope.
The most secure recommendation, AFAIK is to use addEventListener
and postMessage
instead, and use the preload script as a bridge between the renderer and the main scripts.
// 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,
});
这篇关于Electron IPC 和节点集成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!