我正在开发一个 Electron 应用程序(使用 TypeScript、Vite 和 React),允许用户(公司员工)管理存储在服务器数据库中的客户端和文档。应用程序应与服务器通信,向服务器 API 发送 HTTP 请求。我的计划是在 Electron 应用程序中创建一个本地 API,用于发送 HTTP 请求(带有必要的身份验证令牌)。例如,API 将具有
signIn(email, password)
和 getClient(clientId)
等函数。
我的第一个问题是,我将该 API 放在哪里?在主进程中还是在渲染器进程(浏览器窗口)中?该应用程序需要安全,但我也不希望代码变得混乱。
为了找到答案,我查看了其他 Electron 应用程序,发现 GitHub Dekstop(使用 Electron)有一个 API(位于
app/src/lib/api.ts
),可以通过 HTTP 请求与 GitHub API 进行通信。这基本上就是我想做的。分析代码后,在我看来,这个API代码是在渲染器进程上运行的。但是,它还在渲染器进程中使用 Node 包,并将 nodeIntegration
设置为 true
。 Electron 文档 指出:
最重要的是,您不要在任何加载远程内容的渲染器
(BrowserWindow
、WebContentsView
或<webview>
)中启用 Node.js 集成。
它特别指出不要将其用于远程加载的内容。 GitHub 和我的应用程序都使用本地 HTML 文件。那么,在我的应用程序中使用 Node.js 集成(并因此使用 API 的渲染器进程)是否安全?或者我应该在主进程上创建 API(使用 HTTP 请求)并使用 IPC 在渲染器和主进程之间进行通信?
如果我必须与外部服务器通信或发出
HTTP
请求,我会这样做。
我使用
main process
进行 API
通话,使用 IPC
进行沟通。
如果您在渲染器进程中启用
Nodejs integration
,您的应用程序将面临漏洞。例如:攻击者可以运行脚本并可以访问 nodejs api 并窃取令牌或任何敏感数据。
如果您使用主流程,则不会将您的 api 暴露给 UI。并且您可以通过 IPC 在渲染器和主进程之间安全地进行通信。
例如:
在我的
main.js
文件中,我这样做:
import { app, BrowserWindow, ipcMain } from 'electron';
import axios from 'axios';
//handle signIn
async function signIn(email, password) {
const response = await axios.post('https://api.com/signin', { email, password });
return response.data;
}
//handle API request with IPC
function createWindow() {
const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'), //IPC communication
}
});
ipcMain.handle('signIn', async (event, email, password) => {
return await signIn(email, password);
});
}
app.whenReady().then(createWindow);
我并不完全这样做,但你明白我的意思吗?
在我的
preload.js
文件中:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
signIn: (email, password) => ipcRenderer.invoke('signIn', email, password),
//you can also use your other functions here
//for eg: getClient, checkIfUserExists, etc
});
并且在
signIn.js
或渲染器进程中:
import React, { useState } from 'react';
function SignIn() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSignIn = async () => {
try {
const response = await window.api.signIn(email, password);
console.log(response);
} catch (error) {
console.error(error);
}
};
return (
<div>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} />
<button onClick={() => handleSignIn()}>Sign In</button>
</div>
);
}
export default SignIn;
我希望你明白我想说的。
希望这有帮助。