利用Electron开发拦截非授权网络访问的类绿坝浏览器的实践

1. 前言

有道是"每一个非主流的功能实现后面都有一个非主流的业务需求",最近有一个业务需求是提供一款具备类"绿坝"功能的,核心要素是让用户访问少数指定网站,但是不能访问其他互联网网站的浏览器,由于其余一些别的非性能要求不能用市面上的浏览器,只能够用electron开搞。

实现以后将利用Electron实现一个只能访问少数指定网站,拦截其他网络请求的浏览器的方法总结如下。

2. 实现方法

众所周知,Electron里面有一个Chromium内核,从原理上是可以直接作为浏览器应用的。所以要利用Electron实现选择性的网络请求放行/拦截,和浏览器是大同小异的,只不过由于算是定制化的浏览器,我们能操作的空间更多了,除了常规的通过Proxy Server,PAC来筛选网络请求,还能够通过以代码在Electron内每次触发window.opennavigate的时候进行拦截。

2.1 Proxy Server拦截网络请求

常规来说,浏览器是能够通过指定Proxy Server代理服务器和bypass list例外列表来让所有或部分网络请求都通过代理服务器去访问互联网的。

Proxy Server来拦截用户网络请求是比较tricky的,就在于这种方法的核心是指定一个必然不能够生效 的代理服务器,比如http://localhost:13579(端口瞎编一个没有服务的),来拦截任意不应让用户可以访问的网站,并通过指定bypass list让可访问的网站能够例外,不通过这个事实并不存在的代理服务器,而直接连接互联网。

Electron中实现如下:

javascript 复制代码
import { app } from "electron";
// 为app指定一个瞎编的proxy-server地址,从而拦截所有非预期的网络请求
app.commandLine.appendSwitch("proxy-server", "http://127.0.0.1:12580");
// 为用户可访问的互联网地址开通例外规则
app.commandLine.appendSwitch("proxy-bypass-list", "<local>;*foo.com,*.google.com");

如上方代码所示,则在该Electron应用程序中,除了符合<local>;*foo.com,*.google.com规则的网络请求,都会被导向一个事实并不存在的代理服务器,从而被拦截,变相达到了过滤用户网络请求的目的。

2.2 PAC实现规则代理

PAC是一种以JS编写的规则文件,不过理论上流通的现代浏览器已经不接受直接访问PAC文件方式(file://.....),而是要访问网络服务器上的pac文件,哪怕是localhost也行。

因此在electron中,能够以nodejs开启一个简单的HTTP服务器,只提供对这个pac文件的访问。

Electron中设置PAC文件代理的方式:

js 复制代码
import { app } from "electron";
// 为app指定一个网络服务器上的pac文件地址,形如:http://127.0.0.1:6666/proxy.pac
app.commandLine.appendSwitch("proxy-pac-url", config.proxy_pac_url);

简单pac文件写法,用js写的一个函数FindProxyForURL指定规则:

js 复制代码
function FindProxyForURL(url,host){
    // 符合规则,可访问
    if(localHostOrDomainIs(host,"qq.com")){
        return "DIRECT"    
    }
    // 其余不可访问
    return "PROXY localhost:12345"
}

2.3 Electron代码拦截

这种方法主要是在BrowserWindow上面为特定事件绑定处理函数,要注意的是对初始窗口的事件绑定不会继承到被window.open打开的新窗口上,因此每一个窗口都要绑定事件。

对一个BrowserWindow的跳转/打开新窗口的事件绑定如下:

js 复制代码
// 页面内跳转
currentWindow.webContents.on("will-navigate", function (event, url) {
    // checkUrl()是自定义的检查函数,校验不通过后调用event.preventDefault()则可阻止跳转
    if (!checkUrl(url, options)) {
        showWarningDialog();
        event.preventDefault();
    }
});

// 打开新窗口
currentWindow.webContents.setWindowOpenHandler((detail) => {
    let url = detail.url;
    // 校验通过可以访问
    if (checkUrl(url, options)) {
        return {
            action: "allow",
            overrideBrowserWindowOptions: {
                title: "browser window",
                minimizable: BrowserWindowConfig.minimizable,
                webPreferences: {
                    contextIsolation: true,
                    plugins: true,
                },
            },
            outlivesOpener: true,
        };
    }
    // 校验不通过,拒绝打开新窗口
    return { action: "deny" };
});

之所以用setWindowOpenHandler而不是再监听打开新窗口事件后new BrowserWindow()是由于可能有部分新窗口的创建不是GET方法的,在实践上setWindowOpenHandler容错率更高。

3. 小结

经过了我们这个基于Electron的自制"绿坝"的实际应用,我个人认为在拦截的效力及灵活性上是前述第三种方法,在Electron内代码实现的方式是效果最好的,比如上面代码里的checkUrl()方法,可以基于正则写黑名单和白名单,而且方便后期分发的时候做定制化配置,相关设置写在配置文件里不用改源码。

唉,还是成为了曾经自己最讨厌的人。

相关推荐
upgrador4 小时前
桌面应用开发:Electron 与 NSIS 的关系、打包流程及 Windows 本地构建实战
javascript·windows·electron
盒子里的加菲猫1 天前
什么?不会C语言也可以搭建窗口级应用程序?(Electron)
c语言·开发语言·electron
薛定谔的猫-菜鸟程序员3 天前
基于 Electron 的本地短视频解析与下载工具:架构设计与工程实践
java·electron·音视频
imber3 天前
别把多平台发布当复制粘贴:一套可复用的内容工程工作流
electron
imber4 天前
Visual Muse 三平台真实发布验收
electron
前端逗比逗4 天前
Electron 全维度完整配置手册(最新稳定版,适配 Electron 25+)
前端·electron
寒水馨7 天前
Windows下载、安装electron-v43.2.0(附安装包electron-v43.2.0-win32-x64.zip)
javascript·windows·typescript·electron·跨平台·桌面应用·chromium
寒水馨7 天前
macOS下载、安装electron-v43.2.0(附安装包electron-v43.2.0-darwin-arm64.zip)
javascript·macos·electron·node.js·跨平台·桌面应用开发·开源框架
devlei10 天前
Electron原理初探
electron
willyonzhon11 天前
从开发调试到用户能下载安装:我把相册打成安装包的经历
electron·笑笑相册·相册管理·相册开发