Javascript 在与Electron相同的Browser窗口中有两个独立的BrowserViews(在历史记录/cookies/本地存储方面)

Javascript 在与Electron相同的Browser窗口中有两个独立的BrowserViews(在历史记录/cookies/本地存储方面),javascript,google-chrome,browser,electron,Javascript,Google Chrome,Browser,Electron,假设我在同一个浏览器窗口中有两个BrowserView,还有一个UI按钮,允许用户在显示bv1或bv2之间切换(就像Firefox、Chrome等浏览器中的“选项卡”系统,允许您在不同页面之间切换): 当按下按钮(如浏览器中的“选项卡”)时: browserWindow.setBrowserView(bv2); 我注意到这两个BrowserView: 共享相同的Cookie/localStorage(我不想要!),即如果第一个连接到某个帐户,第二个也将连接到同一个帐户 重新启动Electro

假设我在同一个
浏览器窗口中有两个
BrowserView
,还有一个UI按钮,允许用户在显示
bv1
bv2
之间切换(就像Firefox、Chrome等浏览器中的“选项卡”系统,允许您在不同页面之间切换):

当按下按钮(如浏览器中的“选项卡”)时:

browserWindow.setBrowserView(bv2);
我注意到这两个
BrowserView

  • 共享相同的Cookie/localStorage(我不想要!),即如果第一个连接到某个帐户,第二个也将连接到同一个帐户

  • 重新启动Electron应用程序后保留历史记录和cookies(这很好,确实需要!)


问题:如何让这两个
BrowserView
在cookie/localStorage/history方面完全隔离(从而
bv1
可以连接到一个Twitter帐户,而
bv2
可以连接到另一个帐户)?
因此,我设法以非常迂回的方式实现了这一点。有效的会话劫持您自己的会话,将其保存并加载到应用程序关闭/打开。下面的代码带有一些注释,前面有一些有用的链接。当以开发人员身份运行时,以及当使用构建应用程序运行时,这种方法都有效

您可能需要在本地像这样存储cookie来研究可能的安全问题

在这个回答中,我唯一没有解决的问题是:

保持历史。。。重新启动Electron应用程序后


  • -我们使用它来存储/检索cookies。存储的默认位置是
    C:\Users\%user%\AppData\Roaming\%appname%\storage
  • -特别是
    会话.fromPartition
    文档


不确定这是否有效,但您是否尝试了
webPreferences.contextIsolation
?从官方文档来看,它似乎提供了一个独立的javascript上下文,但不确定这是否会影响浏览器视图,但您可以试一试:(文档位于浏览器窗口下,但BrowserView构造函数接受浏览器窗口配置作为参数)。否则,您可以插入会话:(检查webPreferences.session)谢谢@briosheje,我尝试了
contextIsolation
,但没有成功。您有会话注入的代码示例吗?i、 e.如何用不同的会话隔离两个BrowserView?为什么不使用两个BrowserWindow?@Basj调整两个窗口视图的大小不会有太大问题,您可以只听窗口上的调整大小事件,并将两个窗口视图的宽度/高度设置为该值。我下面的答案对解决您的问题有任何帮助吗?@JokerDan它在我明天的待办事项列表中,可以运行它/测试它/提供反馈(今天还没有时间运行代码),非常感谢您发布它!谢谢@JokenDan,它似乎完全有效!即使是
我在这个答案中唯一没有解决的问题是:重新启动后保留历史记录
:它似乎有效!你也喜欢吗?我将继续我的测试,如果有任何东西似乎打破了这一点,我会发帖,但到目前为止,它似乎是有效的。PS:你想用
constcdomain=做什么!cookie.domain.startsWith('.')?'${cookie.domain}”:cookie.domain;cookie.url=https://www${cDomain}
?因此,当我们获取它们时,
url
属性不会填充到cookie上,但是当我们设置它们时,它是必需的属性。因此,在这里,我们将域作为cookie(例如,在某些情况下它是
.twitter.com
,而在另一些情况下它是
auth.twitter.com
)。保存时,它必须是完全限定的URL(
https://www.twitter.com
)-所以我们只是检查
,并从域中正确构建URL,使其成为完整的URL。正如我在评论中所说的,如果你想使用混合了http和https的网站,那么你需要对此进行说明和检查。嗨@Dan_,你还记得你为什么用
eJSON storage
手动存储cookie吗?这似乎是多余的:
session.fromPartition('persist:bv1Session',…)
将自动为我们创建一个持久的
C:\Users\User\AppData\Roaming\appname\Partitions\bv1Session\Cookies
文件。我试图删除
saveCookieState
loadCookieState
,然后。。。它似乎起作用了。你能证实吗?或者可能有一个特定的原因,为什么您使用JSON对Cookie进行了额外的序列化/存储,而不让
fromPartition
为我们做关于Cookie的一切?
browserWindow.setBrowserView(bv2);
const { app, BrowserWindow, BrowserView, globalShortcut, session } = require('electron');
const eJSONStorage = require('electron-json-storage');

// Our two different sesions, views, and base URL for our 'tabs'.
let bv1Session, bv2Session = session;
let bv1, bv2 = BrowserView;
const appTabUrl = 'https://www.twitter.com';

app.on('ready', () => {
  const width = 1200; const height = 600;
  let b1Active = true;

  // Our browser window
  browserWindow = new BrowserWindow({
    width: width,
    height: height,
  });

  // Our first browser window with it's own session instance.
  bv1Session = session.fromPartition('persist:bv1Session', { cache: true });
  bv1 = createBrowserView(appTabUrl, bv1Session, width, height);
  loadCookieState('view1Cookies', bv1Session);

  // Our second browser window with it's own session instance.
  bv2Session = session.fromPartition('persist:bv2Session', { cache: true });
  bv2 = createBrowserView(appTabUrl, bv2Session, width, height);
  loadCookieState('view2Cookies', bv2Session);

  // Our initial setting of the browserview
  browserWindow.setBrowserView(bv1);

  // Our shortcut listener and basic switch mechanic
  // Set to [CTRL + /] for windows or [CMD + /] for OSX
  globalShortcut.register('CommandOrControl+/', () => {
    b1Active ? browserWindow.setBrowserView(bv2) : browserWindow.setBrowserView(bv1);
    b1Active = !b1Active
  });
});

// When the app closes, exit gracefully.
// Unregister keypress listener, save cookie states, exit the app.
app.on('window-all-closed', () => {
  globalShortcut.unregisterAll();
  saveCookieState('view1Cookies', bv1Session);
  saveCookieState('view2Cookies', bv2Session);
  app.quit();
})

// Helper method to generate a browser view.
function createBrowserView(url, session, width, height) {
  let browserView = new BrowserView({
    webPreferences: {
      nodeIntegration: false,
      nodeIntegrationInWorker: false,
      session: session
    }
  });
  browserView.setBounds({ x: 0, y: 0, width: width, height: height });
  browserView.webContents.loadURL(url);
  return browserView;
}

// Method that takes a session name, and our current session to save its state.
function saveCookieState(sessionName, currentSession) {
  currentSession.cookies.get({}, (_, cookies) => {
    cookies.forEach(cookie => {
      // URL is a required paramater, take it from the domain with a little parsing.
      // Twitter always uses HTTPS otherwise, we would need to check for http vs https too.
      const cDomain = !cookie.domain.startsWith('.') ? `.${cookie.domain}` : cookie.domain;
      cookie.url = `https://www${cDomain}`
    });
    // Save the set of cookies against the session name.
    eJSONStorage.set(sessionName, cookies, err => {
      if (err) {
        throw err;
      }
    });
  });
}

// Method that loads a session based on its name, into a session created by us.
function loadCookieState(sessionName, currentSession) {
  eJSONStorage.get(sessionName, (error, cookieData) => {
    // Check for empty object returned, this means no saved sessions.
    if (Object.entries(cookieData).length === 0) {
      return;
    }
    if (error) {
      throw error;
    }
    // If we have saved sessions and no errors, load the sessions.
    cookieData.forEach(cookie => currentSession.cookies.set(cookie, error => {
      if (error) console.error(error);
    }));
  });
}