利用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()方法,可以基于正则写黑名单和白名单,而且方便后期分发的时候做定制化配置,相关设置写在配置文件里不用改源码。

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

相关推荐
明辉光焱6 小时前
[Electron]总结:如何创建Electron+Element Plus的项目
前端·javascript·electron
ZJ_.1 天前
Electron 沙盒模式与预加载脚本:保障桌面应用安全的关键机制
开发语言·前端·javascript·vue.js·安全·electron·node.js
fanxbl9571 天前
Electron 项目实现下载文件监听
javascript·electron·状态模式
怕冷的火焰(~杰)2 天前
创建vue+electron项目流程
vue.js·electron
new出一个对象3 天前
vue3+vite搭建脚手架项目本地运行electron桌面应用
前端·javascript·electron
明辉光焱3 天前
使用yarn,如何编译打包electron?
前端·javascript·electron·node.js
云只上3 天前
Electron + Vue3 开发桌面应用+附源码
前端·javascript·electron
fanxbl9573 天前
Electron 项目中杀掉进程的不同方式
前端·javascript·electron
Liigo4 天前
初次体验Tauri和Sycamore(1)
rust·electron·gui·tauri·wasm·sycamore
yqcoder5 天前
vite-plugin-electron 库作用
服务器·前端·electron