优秀的编程知识分享平台

网站首页 > 技术文章 正文

告别油猴,用Proxyman自动注入JSRPC脚本的黑科技

nanyue 2025-04-27 15:23:51 技术文章 9 ℃

对于前端开发者和自动化测试工程师来说,注入JSRPC脚本是常见的需求。但传统的油猴扩展方式在多浏览器管理和频繁更新代码时显得力不从心。今天,我们将探索一种更高效的方法——通过抓包软件Proxyman自动注入JSRPC脚本,让你的开发和测试工作更加轻松高效!

一、为什么需要自动注入JSRPC脚本?

JSRPC(JavaScript Remote Procedure Call)是一种允许在浏览器中通过WebSocket与后端服务进行通信的技术。它广泛应用于前端开发、自动化测试和浏览器扩展开发中。然而,传统的注入方式(如油猴扩展)存在诸多不便:

  • 每次修改代码都需要手动更新每个浏览器的扩展。
  • 在浏览器集群环境中,管理多个扩展变得复杂且容易出错。
  • 对于一些需要频繁更新的脚本,手动操作效率低下。

二、Proxyman:抓包软件的新玩法

Proxyman是一款强大的抓包软件,它不仅可以捕获和分析网络请求,还提供了强大的脚本功能,允许开发者对请求和响应进行修改。通过Proxyman的脚本功能,我们可以实现JSRPC脚本的自动注入,无需手动操作油猴扩展。

1.Proxyman的脚本功能

Proxyman的脚本功能允许开发者在请求和响应阶段插入自定义代码。具体来说,它提供了两个回调函数:onRequestonResponse,分别在请求到达和响应到达时触发。通过这两个函数,我们可以对请求头、请求参数、请求体以及响应内容进行修改。

2.自动注入JSRPC脚本的原理

自动注入JSRPC脚本的核心思想是在响应阶段修改网页的HTML内容。具体来说,我们可以在网页的<head>标签中插入一个<script>标签,将JSRPC代码嵌入其中。这样,当网页加载时,JSRPC代码会自动执行,无需手动注入。

三、实现自动注入

接下来,我们通过一个具体的例子来展示如何使用Proxyman实现JSRPC脚本的自动注入。

1.编写注入脚本

以下是Proxyman的脚本示例,它会在每个网页的<head>标签中插入JSRPC代码:

async function onRequest(context, url, request) {
    console.log(url);
    return request;
}

async function onResponse(context, url, request, response) {
    console.log(url);
    var jsrpcCode = `
    <script>
    var rpc_client_id, Hlclient = function (wsURL) {
        this.wsURL = wsURL;
        this.handlers = {
            _execjs: function (resolve, param) {
                var res = eval(param);
                if (!res) {
                    resolve("没有返回值");
                } else {
                    resolve(res);
                }
            }
        };
        this.socket = undefined;
        if (!wsURL) {
            throw new Error('wsURL can not be empty!!');
        }
        this.connect();
    };
    Hlclient.prototype.connect = function () {
        if (this.wsURL.indexOf("clientId=") === -1 && rpc_client_id) {
            this.wsURL += "&clientId=" + rpc_client_id;
        }
        console.log('begin of connect to wsURL: ' + this.wsURL);
        var _this = this;
        try {
            this.socket = new WebSocket(this.wsURL);
            this.socket.onmessage = function (e) {
                _this.handlerRequest(e.data);
            };
        } catch (e) {
            console.log("connection failed,reconnect after 10s");
            setTimeout(function () {
                _this.connect();
            }, 10000);
        }
        this.socket.onclose = function () {
            console.log('rpc已关闭');
            setTimeout(function () {
                _this.connect();
            }, 10000);
        };
        this.socket.addEventListener('open', (event) => {
            console.log("rpc连接成功");
        });
        this.socket.addEventListener('error', (event) => {
            console.error('rpc连接出错,请检查是否打开服务端:', event.error);
        });
    };
    Hlclient.prototype.send = function (msg) {
        this.socket.send(msg);
    };
    Hlclient.prototype.regAction = function (func_name, func) {
        if (typeof func_name !== 'string') {
            throw new Error("an func_name must be string");
        }
        if (typeof func !== 'function') {
            throw new Error("must be function");
        }
        console.log("register func_name: " + func_name);
        this.handlers[func_name] = func;
        return true;
    };
    Hlclient.prototype.handlerRequest = function (requestJson) {
        var _this = this;
        try {
            var result = JSON.parse(requestJson);
        } catch (error) {
            console.log("请求信息解析错误", requestJson);
            return;
        }
        if (result["registerId"]) {
            rpc_client_id = result['registerId'];
            return;
        }
        if (!result['action'] || !result["message_id"]) {
            console.warn('没有方法或者消息id,不处理');
            return;
        }
        var action = result["action"], message_id = result["message_id"];
        var theHandler = this.handlers[action];
        if (!theHandler) {
            this.sendResult(action, message_id, 'action没找到');
            return;
        }
        try {
            if (!result["param"]) {
                theHandler(function (response) {
                    _this.sendResult(action, message_id, response);
                });
                return;
            }
            var param = result["param"];
            try {
                param = JSON.parse(param);
            } catch (e) {
            }
            theHandler(function (response) {
                _this.sendResult(action, message_id, response);
            }, param);
        } catch (e) {
            console.log("error: " + e);
            _this.sendResult(action, message_id, e);
        }
    };
    Hlclient.prototype.sendResult = function (action, message_id, e) {
        if (typeof e === 'object' && e !== null) {
            try {
                e = JSON.stringify(e);
            } catch (v) {
                console.log(v);
            }
        }
        this.send(JSON.stringify({"action": action, "message_id": message_id, "response_data": e}));
    };
    window.Hlclient = Hlclient;
    var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");
    </script>
    `;
    response.body = response.body.replace('<head>', '<head>' + jsrpcCode);
    return response;
}

2.配置Proxyman

将上述代码粘贴到Proxyman的脚本编辑器中,并为当前规则设置一个有意义的名称(如“JSRPC Injector”)。同时,设置匹配规则,确保只对需要的网页生效,而不是对所有网页都注入脚本。

3.测试效果

完成配置后,刷新目标网页,即可看到JSRPC脚本自动注入的效果。此时,无需手动打开浏览器控制台,JSRPC代码会自动连接到指定的服务端,并在网页中生效。

四、更多用法

Proxyman的脚本功能不仅限于注入JSRPC脚本。它还可以用于:

  • Mock API接口:通过脚本动态返回响应,方便测试API接口。
  • 修改请求和响应数据:对请求头、请求参数、请求体以及响应内容进行修改,满足各种开发和测试需求。

五、总结

通过Proxyman自动注入JSRPC脚本的方法,不仅解决了传统油猴扩展的诸多不便,还提高了开发和测试的效率。这种方法适用于几乎所有网站,尤其适合需要频繁更新脚本或使用浏览器集群的场景。如果你对这种方法感兴趣,不妨尝试一下,让开发和测试工作更加轻松高效!

今天,我们探讨了如何通过Proxyman自动注入JSRPC脚本,这种方法不仅高效便捷,还解决了传统油猴扩展的诸多痛点。希望这篇文章能为你带来新的启发,让你在开发和测试工作中更加得心应手。如果你有任何疑问或建议,欢迎在评论区留言,我们一起交流!

Tags:

最近发表
标签列表