Chrome 浏览器插件 Manifest V3 版本,拦截获取Ajax请求XHR/Fetch

admin 教程 9
Chrome 浏览器插件 Manifest V3 版本,拦截获取Ajax请求XHR/Fetch-第1张图片-木龙笔记
teste

首先

本插件目的是获取指定Ajax请求返回的内容,及该请求的url地址。

Manifest V3

本文使用 Chrome 插件使用V3版本,V3较V2在一些配置项上稍有变动。

2024 年 6 月开始在 Chrome 127 及更高版本中禁用预稳定版 Chrome (Dev、Canary 和 Beta)中的 Manifest V2 扩展。

原理

通过重写 XHR 和 Fetch 请求,拦截所需要的内容,把内容发送到指定url地址。

如下图,xhr、fetch请求已由自定义的 xhr_script.js 文件接管。

Chrome 浏览器插件 Manifest V3 版本,拦截获取Ajax请求XHR/Fetch-第2张图片-木龙笔记
network-1

如下图,拦截的内容发送至本地的 test.php 文件。

Chrome 浏览器插件 Manifest V3 版本,拦截获取Ajax请求XHR/Fetch-第3张图片-木龙笔记
network-2

代码

这里贴上插件中的部分代码:

(function(xhr) {  var XHR = xhr.prototype;  var open = XHR.open;  var send = XHR.send;  // 对open进行patch 获取url和method
  XHR.open = function(method, url) {    this._method = method;    this._url = url;    return open.apply(this, arguments);
  };  // 同send进行patch 获取responseData.
  XHR.send = function(postData) {    this.addEventListener('load', function() {      var myUrl = this._url ? this._url.toLowerCase() : this._url;      if(myUrl ) {        if ( this.responseType != 'blob' && (this.responseType).indexOf('video')<0 && this.responseText) {          // responseText is string or null
          try {            var arr = this.responseText;            //console.log('请求:', this._url );
            // 因为inject_script不能直接向background传递消息, 所以先传递消息到content_script
            window.postMessage({'url': this._url, "response": arr}, '*');

          } catch(err) {            console.log(err);            console.log("Error in responseType try catch");
          }
        }
      }
    });    return send.apply(this, arguments);
  };
})(XMLHttpRequest);

(function () {    console.log('====fetch====' );    
    //重写fetch
    const fetch_tools = {
        
        originalFetch: window.fetch.bind(window),
        myFetch: function (...args) {            console.log('fetch==', args);            const getOriginalResponse = async (stream) => {              let text = '';              const decoder = new TextDecoder('utf-8');              const reader = stream.getReader();              const processData = (result) => {                if (result.done) {                  return text;
                }                const value = result.value;
                text += decoder.decode(value, {stream: true});                return reader.read().then(processData);
              };              return await reader.read().then(processData);
            }            //const [requestUrl, data] = args;

            //console.log('==requestUrl==', requestUrl);
            //console.log('==data==', data);

            let url = args[0];            //测试
            if (url.includes('data.bilibili.com/v2/log/web') ) {                console.log('###拒绝###');                return Promise.reject('error');
            }            return fetch_tools.originalFetch(...args).then(async  (response) => {                
                //window.postMessage({'url': this._url, "response": arr}, '*');
                
                const originalResponse = await getOriginalResponse(response.body);                console.log('==originalResponse==', response );                window.postMessage({'url': response.url, "response": originalResponse }, '*');                if (originalResponse) {                  const stream = new ReadableStream({
                    start(controller) {
                      controller.enqueue(new TextEncoder().encode(originalResponse));
                      controller.close();
                    }
                  });                  const newResponse = new Response(stream, {
                    headers: response.headers,
                    status: matchedInterface && matchedInterface.replacementStatusCode || response.status,
                    statusText: response.statusText,
                  });                  const responseProxy = new Proxy(newResponse, {
                    get: function (target, name) {                      switch (name) {                        case 'body':                        case 'bodyUsed':                        case 'ok':                        case 'redirected':                        case 'type':                        case 'url':                          return response[name];
                      }                      return target[name];
                    }
                  });                  for (let key in responseProxy) {                    if (typeof responseProxy[key] === 'function') {
                      responseProxy[key] = responseProxy[key].bind(newResponse);
                    }
                  }                  return responseProxy;
                }                return response;
            });
        },
    }    window.fetch = fetch_tools.myFetch;
})();

更多

插件完整代码下载地址:https://wwz.lanzout.com/iYohv1p9065c

一些参考链接及源代码地址:

Chrome 扩展V3 中文文档:https://doc.yilijishu.info/chrome/

Manifest V2 支持时间表:https://developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline

两万+字数:从0到1带你开发 Chrome 浏览器 Manifest V3 版本插件:https://blog.csdn.net/guoqiankunmiss/article/details/135652524

Chrome 浏览器插件从 Manifest V2 升级到 V3 版本所需要修改的点:https://guoqiankun.blog.csdn.net/article/details/135552664

记录Chrome插件从V2版本升级到V3版本的过程中遇到的问题:https://blog.csdn.net/lxm1353723767/article/details/127706101

XHR:https://github.com/zx69/request-retransmission-chrome-extension/blob/master/myXHRScript.js

Fetch:https://github.com/PengChen96/ajax-tools/blob/master/pageScripts/index.js

使用 Chrome 插件拦截广告的原理:https://www.cnblogs.com/yangzhou33/p/13835409.html


你可能想看:

标签: Chrome 浏览器插件

发布评论 0条评论)

还木有评论哦,快来抢沙发吧~