Electron 网络拦截实战

1,610 阅读2分钟

Electron 提供的 webRequest API,允许开发者对网络进行过滤和监听,并且可以修改 header 字段甚至请求地址,功能非常强大,它的类结构如下:

Electron 提供的 [webRequest API]

不过需要注意,该模块只能在主进程中使用,接下来为大家介绍 webRequest 三个非常典型的使用场景:

一 绕过跨域限制

使用 Electron 加载第三方网站的时候,默认行为和 Chrome 是一致的,都会受到同源策略的影响。

  • 使用nodejs搭建测试服务器,启动3000和4000端口。此时在控制台请求 3000 端口上的服务,会发现典型的跨域报错,果不其然被 cors 策略拒绝了:

  • 没有配置前,在渲染进程中的控制台调用接口

fetch('http://localhost:3000').then(it=>it.json()).then(it=>console.log(it))
  • 报错 image.png
  • 主进程中设置请求转发
// 绕过跨域限制
const mainWindow = CoreWindow.getMainWindow();

mainWindow.webContents.session.webRequest.onHeadersReceived(

{

urls: ["http://localhost:*/*"],

},

(details, callback) => {

const { responseHeaders } = details;

responseHeaders["Access-Control-Allow-Origin"] = ["*"];

callback({ responseHeaders });

}

);

image.png

请求转发

  • 通过 webRequest API,可以将发到接口 A 的请求转发到接口 B。
  • 为了验证这个能力,我们写了一个 http 服务,同时监听 3000 和 4000 端口,并响应 JSON 数据,里面包含了 port 字段表示当前请求到了哪个端口。先上效果图。

image.png

image.png

// 设置网络代理

setProxy() {

if (!this.setProxyFlag) {

const mainWindow = CoreWindow.getMainWindow();

const sessionItem = mainWindow.webContents.session;

// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ywxd-ee/1.0.1 Chrome/106.0.5249.199 Electron/21.4.4 Safari/537.36

const UserAgent = sessionItem.getUserAgent();

xdLogInfo(`[basehttp:setProxy] UserAgent = ${UserAgent}`);

// 自定义 UA

const filter = {

urls: ["http://localhost:3000/*"],

};

// 自定义 UA

// 有些接口为了过滤非法请求,会首先校验 UserAgent,正常的浏览器是无法伪造 UA 的,

//不过在 Electron 里面可以很容易做到,webRequest 模块提供的 onBeforeSendHeaders 方法能够对域名进行过滤,

// 拦截即将发出去的请求头,修改之后再真正的发出去。

  


mainWindow.webContents.session.webRequest.onBeforeSendHeaders(

// filter,

{

urls: ["http://localhost:3000/*"],

},

(details, callback) => {

details.requestHeaders["User-Agent"] = "MyAwesomeAgent";

callback({ requestHeaders: details.requestHeaders });

}

);

// 请求转发,通过 webRequest API,可以将发到接口 3000 的请求转发到接口 4000。

mainWindow.webContents.session.webRequest.onBeforeRequest(

// filter,

{

urls: ["http://localhost:3000/*"],

},

(details, callback) => {

// callback({ redirectURL: "http://localhost:4000" });

// 使用正则表达式提取原请求的 path 和 query

const url = new URL(details.url);

const pathAndQuery = url.pathname + url.search;

// 构建新的 URL,替换 IP 和端口

const redirectURL = `http://localhost:4000${pathAndQuery}`;

console.log(`[basehttp:setProxy] redirectURL = ${redirectURL}`);

callback({ redirectURL });

}

);

// 绕过跨域限制

mainWindow.webContents.session.webRequest.onHeadersReceived(

{

urls: ["http://localhost:*/*"],

},

(details, callback) => {

const { responseHeaders } = details;

responseHeaders["Access-Control-Allow-Origin"] = ["*"];

callback({ responseHeaders });

}

);

}

}
// server.js

const express = require("express");

const URL = require('url')

let count = 1;

// 封装成一个方法

function startServer(port = 3000) {

const app = express();

// 定义一个 GET 路由

app.get("/", (req, res) => {

console.log(`Server is running on http://localhost:${port}`);

const { url, method, headers } = req

const { query, pathname } = URL.parse(url, true)

const sendData = { method, pathname, query, headers, port };

count ++;

console.log(` &&&&&&&&&&& ****************** `);

console.log(`originalUrl = ${req.originalUrl}`);

console.log(`query = ${JSON.stringify(req.query)}`);

console.log(` count=${count}, ${port} port收到请求 ----- ${JSON.stringify(sendData)}`);

res.setHeader('Content-Type', 'application/json')

res.end(JSON.stringify(sendData))

});


// 调测接口转发请求

app.get("/xd/:platform", (req, res, next) => {

const { url, method, headers } = req

const { query, pathname } = URL.parse(url, true)

const sendData = { method, pathname, query, headers, port };

count ++;

console.log(` ****************** `);

console.log(` count=${count}, ${port} port收到请求 -----`);

console.log(` ${port} port收到请求 ----- ${JSON.stringify(sendData)}`);

  


let { platform } = req.params;

console.log(`originalUrl = ${req.originalUrl}`);

console.log(`query = ${JSON.stringify(req.query)}`);

console.log(`platform = ${platform}`);

let resinfo = null;

resinfo = { code: 0, msg: "成功",

"path": "xd-mac-1.0.1-arm64.zip",

"releaseDate": "2024-05-07T10:46:31.141Z",

"sha512": "vFlUCW+SdhNvyvY42TIVYwn8FWkkMd9pxvbrovZ0mZREJk+dinrNo13VhbqV7drMu3suGg7LOSZ7XAwdK633dw==",

"version": "1.0.1"

};

res.send(resinfo);

});

  


// 启动服务器

app.listen(port, () => {

console.log(`Server is running on http://localhost:${port}`);

});

}

  


// 导出方法

module.exports = {

startServer,

};

// index.js
// 启动服务器

startServer(3000);

startServer(4000);

参考

作者:乔珂力
链接:juejin.cn/post/720810…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。