Electron跨域问题

806 阅读2分钟

背景——在开发Electron的过程中,我遇到了一个不太了解的问题:一个HTTP请求返回了403状态码,详细查看response信息显示是Cross-Origin Resource Sharing (CORS) Filter在作怪,提示“CORS origin denied:xxx”。即使我尝试在工程中设置webSecurity: false,问题依旧存在。

原因分析

Electron架构中包含两种主要进程:主进程(NodeJS进程)和渲染进程(Chromium进程)。渲染进程发起的网络请求会受到浏览器的CORS(跨源资源共享)限制,就像被设置了一道看不见的屏障。通过Postman和Developer Tools的深入分析,我发现:

  • 对于简单请求(HEAD、GET、POST):会在请求头中添加0rigin"字段,成功响应的网络请求通常会在响应头中包含 ACCess-ontrol-ALlow-0rigin 字段,用于指定允许跨域访问的源:

    a.Access-Control-Allow-Origin(必需):这是允许跨域的关键字段。

    b.Access-Control-Allow-Credentials(可选):决定是否允许发送Cookie。

  • 去除“Origin”字段:我发现,如果手动删除请求头中的“Origin”字段,请求就能成功访问目标服务。同样,直接在浏览器中打开该URL也不会包含“Origin”字段。

解决策略

  • 渲染进程受限?交给主进程处理!

    既然渲染进程受到限制,我们可以将网络请求转移到主进程中处理。利用IPC(进程间通信)机制,主进程和渲染进程可以高效沟通。在主进程中使用 ipcMain, 在渲染进程中使用 ipcRenderer,可以实现事件的发送和监听。

    a.渲染进程的操作:

    当渲染进程需要获取响应时,会调用 ipcRederer.invoke() 方法,发送一个get-response事件到主进程,并等待响应。

    import { ipcRenderer } from 'electron';
    
    //……
        try{
            const url = 'xxx';
            let json: any = ();
            json = await ipcRenderer.invoke('get-response',url, host);
    
            //……
        } catch (error){
            //……
        }
    

    b.主进程的操作:

    在主进程中,使用 ipcMain.handle() 方法来处理请求,并返回一个Promise。

    import { ipcMain } from 'electron';
    const https = require('https');
    
    //……
    app.on('ready',()=> {
        //……
        ipcMain.handle('get-response', async (event,url)=> {
            try {
                const data= await new Promise((resolve, reject) => {
                    //……
                });
    
                return data;
            } catch (error) {
                //……
            }
        });
    });
    
    

    PS:主进程的网络请求不会在开发者工具当中看到。

  • 去除“Origin”字段

    如果"Origin"字段是罪魁祸首,那我们可以使用 webRequest API 在请求发送前悄悄修改它。

    import { session } from 'electron';
    
    //……
        session.defaultsession.webRequest.onBeforeSendHeaders((details, callback) => {
            if (details.url.startsWith('xxx')) {
                delete details.requestHeaders['Origin'];
                // 或者将Origin设置为允许的值
                // details.requestHeaders['Origin'] = 'https://allowed-origin.com';
            }
            callback({ requestHeaders: details.requestHeaders });
        });
    //……
    

    PS: session需要在主进程中使用,在渲染进程中,session是undefined。在我的场景中,请求头中多了“Origin”字段,也可以将“Origin”设置为域名白名单中的内容。

  • 找服务端解决

    (虽然这个方法听起来最简单,但我跟服务端的同学不太熟😂😂


不管怎样,问题总算是解决啦,每天都会遇到不懂的问题,害(流泪猫猫头o(TヘTo)