問題描述
因此,我遵循了許多指南來設(shè)置 Webpack、Electron 和 React 以制作桌面應(yīng)用程序.完成設(shè)置后,我開始工作,并了解到我需要來自 main 和 renderer 的 IPC 機制才能進(jìn)行通信.
從electron"導(dǎo)入{ipcRenderer};
將此添加到我的 renderer.js 文件會導(dǎo)致錯誤 Uncaught ReferenceError: require is not defined
.
在向一些同事提出我的問題后,有人建議我在我的 main.js 文件中進(jìn)行更改
webPreferences: {節(jié)點集成:假,}
到
webPreferences: {節(jié)點集成:真,}
我在谷歌上讀到的所有地方都非常清楚地表明,如果安全是你關(guān)心的事情,那么這不是你應(yīng)該做的事情.但是,我能遇到的有關(guān)電子 ipc 的所有資源都使用了 ipcRenderer.
現(xiàn)在,互聯(lián)網(wǎng)上的每個示例是否都存在巨大的安全漏洞,還是我在這里遺漏了一些關(guān)鍵部分?
我的問題如下.
- 是否可以在不啟用 nodeIntegration 的情況下使用 ipcRenderer?
- 如果是,我該怎么做,為什么這么多資源會排除這些信息?
- 如果不是,我用什么?
如果我問錯了問題,或者我遺漏了什么,或者我提出這個問題的方式有任何其他明顯問題,請告訴我,否則提前致謝.
- 是否可以在不啟用 nodeIntegration 的情況下使用 ipcRenderer?
這是可能的,但很繁瑣.這可以通過使用 preload
腳本來完成.
- 如果是,我該怎么做,為什么這么多資源會排除這些信息?
可以使用 preload
腳本,如下所示.但是,這是 不安全.大多數(shù)現(xiàn)有文檔都沒有展示最佳安全實踐.
后面會給出一個更安全的例子.
//preload.js常量電子 = 要求('電子');process.once('加載', () => {global.ipcRenderer = 電子.ipcRenderer;});
//main.jsconst {app, BrowserWindow} = 要求('電子');app.on('準(zhǔn)備好了', () => {//創(chuàng)建瀏覽器窗口.贏=新瀏覽器窗口({backgroundColor: '#fff',//始終設(shè)置背景顏色以啟用字體抗鋸齒!網(wǎng)絡(luò)偏好:{預(yù)加載:path.join(__dirname, './preload.js'),節(jié)點集成:假,啟用遠(yuǎn)程模塊:假,//contextIsolation: true,//nativeWindowOpen: true,//沙盒:真,}});win.loadURL(`file://${path.join(__dirname, 'index.html')}`);
注意預(yù)加載腳本的路徑必須是絕對路徑,這也可以使用 webpack/babel 時會變得復(fù)雜,因為輸出文件可能是不同的路徑.
<塊引用>- 如果不是,我用什么?
編輯正如@Yannic 所指出的,Electron 現(xiàn)在支持另一個選項,稱為 contextBridge
.這個新選項可以更簡單地解決問題.有關(guān) contextBridge
的信息,請查看電子文檔:https://www.electronjs.org/docs/tutorial/context-isolation
但是,即使使用 contextBridge
,您也不應(yīng)該嘗試公開整個電子 API,而只是為您的應(yīng)用設(shè)計的有限 API
如前所述,雖然可以使用如上所示的 ipcRenderer,但當(dāng)前的電子安全建議也建議啟用 contextIsolation
.這將使上述方法無法使用,因為您無法再將數(shù)據(jù)添加到全局范圍.
最安全的建議,AFAIK 是使用 addEventListener
和 postMessage
代替,并使用預(yù)加載腳本作為渲染器和主腳本之間的橋梁.
//preload.js常量 { ipcRenderer } = 要求('電子');process.once('加載', () => {window.addEventListener('消息', 事件 => {//用自定義事件做一些事情常量消息 = 事件數(shù)據(jù);if (message.myTypeField === 'my-custom-message') {ipcRenderer.send('自定義消息', message);}});});
//main.jsconst {app, ipcMain, BrowserWindow} = require('electron');app.on('準(zhǔn)備好了', () => {ipcMain.on('custom-message', (event, message) => {console.log('得到一個IPC消息', e, message);});//創(chuàng)建瀏覽器窗口.贏=新瀏覽器窗口({backgroundColor: '#fff',//始終設(shè)置背景顏色以啟用字體抗鋸齒!網(wǎng)絡(luò)偏好:{預(yù)加載:path.join(__dirname, './preload.js'),節(jié)點集成:假,啟用遠(yuǎn)程模塊:假,上下文隔離:真,沙盒:真,//nativeWindowOpen: true,}});win.loadURL(`file://${path.join(__dirname, 'index.html')}`);
//renderer.jswindow.postMessage({myTypeField: '我的自定義消息',一些數(shù)據(jù):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,
});
這篇關(guān)于Electron IPC 和節(jié)點集成的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!