chrome插件进行请求转发

4,433 阅读6分钟

一、基本介绍

chrome扩展又称为chrome插件,是一个用web技术开发、用来增强浏览器功能的软件,它其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包。

chrome插件没有严格的项目结构要求,它只要求根目录下有一个manifest.json的文件即可。

从右上角菜单->更多工具->扩展程序可以进入 插件管理页面,也可以直接在地址栏输入 chrome://extensions 访问。如下图。

image.png

勾选开发者模式即可以文件夹的形式直接夹在插件,否则只能安装.crx格式的文件。Chrome要求插件必须从它的Chrome应用商店安装,其它任何网站下载的都无法直接安装,所以,其实我们可以把crx文件解压,然后通过开发者模式直接加载。

开发中,代码有任何改动都必须重新加载插件,只需要在插件管理页按下Command+R即可,以防万一最好还把页面刷新一下。也可以直接点击插件的刷新按钮,本地的代码即可同步。如下图。

image.png

二、核心介绍

2.1 manifest.json

{
  "name": "SourceMapRedirect", // 插件名称
  "description": "Try to redirect sourcemap to ftp server", // 插件描述
  "version": "1.0", // 插件版本
  "manifest_version": 2, // 清单文件的版本,必填
  "permissions": [ // 权限申请(基本上需要使用的chrome插件api都需要进行权限申请)
    "webRequest",
    "webRequestBlocking",
    "<all_urls>", //使用到的url也需要进行权限申请,也可以写成 "http://*/*", "https://*/*",可                    以通过executeScript或者insertCSS访问的网站
    "declarativeNetRequest"
  ],
  "background": { // 会一直常驻后台的JS或后台页面
    "scripts": [
      "background.js"
    ]
    // "page": "background.html" // 两种指定方式,如果指定js,那么会自动生成一个背景页
  },
 "content_scripts": [
   {
     //"matches": ["http://*/*", "https://*/*"],
      // "<all_urls>" 表示匹配所有地址
      "matches": ["<all_urls>"],
      // 多个JS按顺序注入
      "js": ["js/jquery-1.8.3.js", "js/content-script.js"],
      // JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式
      "css": ["css/custom.css"],
      // 代码注入的时间,
      // 可选值: "document_start", "document_end", or "document_idle"
      // 最后一个表示页面空闲时,默认document_idle
      "run_at": "document_start"
   }
 ]
  "icons": { // 图标,一般偷懒全部用一个尺寸也没啥问题
    "16": "/images/icon16.png",
    "32": "/images/icon32.png",
    "48": "/images/icon48.png",
    "128": "/images/icon128.png"
  },
  "action": {
    "default_popup": "popup.html", // 浏览器右上角图标点击弹窗页面
    "default_icon": { //浏览器右上角图标
      "16": "/images/get_started16.png",
      "32": "/images/get_started32.png",
      "48": "/images/get_started48.png",
      "128": "/images/get_started128.png"
    }
  },
 
  "options_page": "options.html", // chrome40以前的插件配置页写法
  "options_ui": { // chrome40以后的插件配置写法,如果两个都写,新版chrome只认后面这一个
      "page": "options.html",
      // 添加一些默认的样式,推荐使用
      "chrome_style": true
   },
   "declarative_net_request": { // 一个用于拦截请求的chrome api,暂时不用管它
    "rule_resources": [{
      "id": "ruleset_1",
      "enabled": true,
      "path": "rules.json"
    }]
  },
}

manifest_version我用的是版本2,目前已经有3版本,且官方是比较推荐使用3版本的。但是由于我想要使用的webRequest api对于3有支持性问题报错,所以我使用了2版本。且目前官网大部分例子都是2版本,所以对于初学者,2版本相对更加友好一些。

content-scripts是chrome插件向页面注入脚本的一种形式。它和原始页面共享DOM,但是不共享JS,如要访问页面JS(例如某个JS变量),只能通过injected js来实现。content-scripts不能访问大部分chrome.xxx.api,除了下面这4种:

  • chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
  • chrome.i18n
  • chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
  • chrome.storage

2.2 background

background是一个常驻页面,它的生命周期是插件中所有类型页面中最长的,随着浏览器打开而打开,随着浏览器关闭而关闭,所以通常会把需要一直运行的全局的代码放在其中。它的权限很高,几乎可以调用所有的chrome扩展api(除了devtools)

它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。

可以通过page指定一张网页,也可以通过scripts直接指定一个JS,chrome会自动为这个js生成一个默认的网页。

需要特别说明的是,虽然你可以通过chrome-extension://xxx/background.html直接打开后台页,但是你打开的后台页和真正一直在后台运行的那个页面不是同一个,换句话说,你可以打开无数个background.html,但是真正在后台常驻的只有一个,而且这个你永远看不到它的界面,只能调试它的代码。

2.3 event-pages

鉴于background生命周期太长,长时间挂载后台可能会影响性能,所以Google又弄一个event-pages,在配置文件上,它与background的唯一区别就是多了一个persistent参数:

{
  "background": {
    "scripts": ["event-page.js"],
    "persistent": false
  }
}

它的生命周期是:在被需要时加载,在空闲时被关闭,什么叫被需要时呢?比如第一次安装、插件更新、有content-script向它发送消息,等等。

除了配置文件的变化,代码上也有一些细微变化,这个简单了解一下就行了,一般情况下background也不会很消耗性能的。

三、请求转发API

// background.js
​
chrome.webRequest.onBeforeRequest.addListener(
  (info) => {
    if (info.url.includes(".map")) {
      console.log("source map files: " + info.url);
    }
    return { redirectUrl: XXX } // 希望重定向的地址
    // 如果需要拦截请求可以返回{cancel: true}
  },
  // filters
  {
    urls: [
      "<all_urls>"
    ]
  },
  // extraInfoSpec
  ["blocking"]
);

我其实最开始想处理的是针对sourcemap的安全问题。希望把sourcemap文件上传到内网并从公网删除。之后调试的时候将公网发起的sourcemap请求转发到内网,从而在保证安全的同时也可以方便调试。

在实际实现的时候发现一个致命的问题,请求的拦截转发是非常好实现的。但是谷歌浏览器根据js末尾的注释代码# sourceMappingURL=bundle.js.map自动发起的sourcemap请求是无法通过webRequest api抓到的。declarative_net_request api也尝试了一下,也是无法抓到的。我猜测可能的原因是谷歌浏览器本身在network面板中会忽略掉sourcemap的自动请求,(注意,google浏览器仅在开发面板打开时才会自动请求sourcemap文件),因此提供的api无法抓包,也是可以理解的(我就是这样说服自己的。。。。)当然啦,把sourcemap文件地址输入到地址栏,主动发起的请求,network面板中可以抓到,chrome api也可以。

鉴于以上原因,想要用chrome实现sourcemap的转发请求,也就无法实现了。但是关于chrome插件的开发还是学到了一些东西,可以抓包到的请求可以非常轻松的实现转发。

最终方案:使用whistle脚本自动添加转发规则。

由于whislte, charles这些抓包工具是可以抓去到谷歌浏览器自动发起的sourcemap请求,那么就简单点,写规则转发吧。为了方便维护,添加whistle脚本在代码中,这样只要运行脚本,即可添加转发规则。

// .whistle.js
​
const pkg = require('./package.json');
​
let projectDomains = ['XXX', 'XXXX', 'XXXX']
let sourceMapFolder = 'XXXXX'
let ftp_ip = 'XXXXX'exports.name = `[${pkg.name}]本地环境配置`;
exports.rules = `
/[http|https]://[^/]*(${projectDomains.join('|')})/(.+.map)/  ${ftp_ip}/${sourceMapFolder}/$2
`;
// package.json
{
  ...
  "scripts": {
    ...
     "add_whistle_config": "w2 add --force",
    ...
  }
  ...
}

参考文档

www.cnblogs.com/liuxianan/p…

www.bookstack.cn/read/chrome…

developer.chrome.com/docs/extens…

github.com/GoogleChrom…