黑基网 首页 电脑 网页浏览器 查看内容

【Chrome扩展开发】定制HTTP请求响应头域

2017-11-15 01:49| 投稿: lofor |来自: 互联网

摘要: 目录本文首发于《程序员》杂志2017年第9、10、11期,下面的版本又经过进一步的修订。导读搜索是程序员的灵魂,为了提升搜索的效率,以便更快的查询信息,我试着同时搜索4个网站,分别是百度、Google、维基、Bing。一 ...

目录

本文首发于《程序员》杂志2017年第9、10、11期,下面的版本又经过进一步的修订。

导读

搜索是程序员的灵魂,为了提升搜索的效率,以便更快的查询信息,我试着同时搜索4个网站,分别是百度、Google、维基、Bing。一个可行的做法就是网页中嵌入4个iframe,通过js拼接前面4个搜索引擎的Search URL并依次在iframe中加载。这个构思丝毫没有问题,简单粗暴。然而就是这么简单的功能,也无法实现。由于Google网站在HTML的response header中添加了 X-Frame-Options 字段以防止网页被Frame(这项设置常被用来防止Click Cheats),因此我无法将Google Search加入到iframe中来。那么,我会放弃Google吗?

Nginx反向代理Google

显然不会,既然问题出在 X-Frame-Options 上,我去掉就行了。对于请求或响应头域定制,nginx是个不错的选择,其第三方的 ngx_headers_more 模块就特别擅长这种处理。由于nginx无法动态加载第三方模块,我动态编译了nginx以便加入 ngx_headers_more 模块。至此,第一步完成,以下是nginx的部分配置。

location / {
  more_clear_headers 'X-Frame-Options';
}

为了让 www.google.com 正常访问,我需要使用另外一个域名比如 louis.google.com 。通过nginx,让 louis.google.com 转发到 www.google.com ,转发的同时去掉响应头域中的 X-Frame-Options 字段。于是nginx配置看起来像这样:

server {
  listen 80;
  server_name louis.google.com;
  location / {
    proxy_pass https://www.google.com/;
    more_clear_headers 'X-Frame-Options';
  }
}

以上的配置有什么问题吗?且不说http直接转https的问题,即使能转发,实际上由于Google的安全策略限制,我们也访问不了Google首页!

最终我使用了一个Nginx Google代理模块 ngx_http_google_filter_module ),nginx配置如下:

server {
    listen 80;
    server_name louis.google.com;
    resolver 192.168.1.1; # 需要设置为当前路由的网关
    location / {
        google on;
        google_robots_allow on;
        more_clear_headers 'X-Frame-Options';
    }
}

以上,通过实现一个Google网站的反向代理,代理的同时去掉了响应头域中的 X-Frame-Options 字段。至此,nginx方案完结。

nginx方案有一个明显的缺陷是,配置中resolver对应的网关IP 192.168.1.1 是随着路由器的改变而改变的,家里和公司就是两个不同的网关(更别说去星巴克了办公了),因此经常需要手动去修改网关然后重启nginx。

IHeader缘起

nginx方案的这个缺陷多少有些麻烦,恰好Chrome Extension可以定制headers,为了解决这个问题,我便尝试开发Chrome Extension。(使用Chrome以来,我下载试用过无数的Chrome Extension。每每看到一款优秀的Extension,都要激动好久,总有一种相见恨晚的感觉。Extension以其强大的定制能力,神奇的运行机制征服了无数的开发者,我也不例外。然而无论多少次的学习和模仿,最终的目的还是为了使用,故开发一款定制请求的Extension势在必行。)由于Chrome浏览器与网页的天然联系,使用Chrome Extension的方式去掉响应头域字段,比其它方案要更加简单高效。

要知道,Chrome Extension提供的API中有 chrome.webRequest.onHeadersReceived 。它能够添加对响应头的监听并同步修改响应头域,去掉 X-Frame-Options 似乎是小case。

于是新建项目,取名 IHeader 。目录结构如下:

其中,_locales是国际化配置,目前IHeader支持中文和英文两种语言。

res是资源目录,index.html是extension的首页,options.html是选项页面。

manifest.json是extension的声明配置(总入口),在这里配置extension的名称、版本号、图标、快捷键、资源路径以及权限等。

manifest.json贴出来如下:

{
  "name": "IHeader", // 扩展名称
  "version": "1.1.0", // 扩展版本号
  "icons": { // 上传到chrome webstore需要32px、64px、128px边长的方形图标
    "128": "res/images/lightning_green128.png",
    "32": "res/images/lightning_green.png",
    "64": "res/images/lightning_green64.png"
  },
  "page_action": { // 扩展的一种类型,说明这是页面级的扩展
    "default_title": "IHeader", // 默认名称
    "default_icon": "res/images/lightning_default.png", // 默认图标
    "default_popup": "res/index.html" // 点击时弹出的页面路径
  },
  "background": { // 扩展在后台运行的脚本
    "persistent": true, // 由于后台脚本需要持续运行,需要设置为true,反之扩展不活动时可能被浏览器关闭
    "scripts": ["res/js/message.js", "res/js/background.js"] // 指定运行的脚本,实际上Chrome会启用一个匿名的html去引用这些js脚本。等同于"pages":["background.html"]这种方式(注意这两种互斥,同时设置时,后一种有效)
  },
  "commands": { // 指定快捷键
    "toggle_status": { // 快捷命令的名称
      "suggested_key": { // 快捷命令的热键
        "default": "Alt+H",
        "windows": "Alt+H",
        "mac": "Alt+H",
        "chromeos": "Alt+H",
        "linux": "Alt+H"
      },
      "description": "Toggle IHeader" // 描述
    }
  },
  "content_scripts": [ // 随着每个页面加载的内容脚本,通过它可以访问到页面的DOM
    {
      "all_frames": false, // frame中不加载
      "matches": ["\u003Call_urls>"], // 匹配所有URL
      "js": ["res/js/message.js", "res/js/content.js"] // 内容脚本的路径
    }
  ],
  "default_locale": "en", // 默认语言
  "description": "__MSG_description__", // 扩展描述
  "manifest_version": 2, // Chrome 18及更高版本中,应该指定为2,低于v18版本的Chrome浏览器可以指定为1或不指定
  "minimum_chrome_version": "26.0", // 最低支持到v26版本,主要受制于chrome.runtime api
  "options_page": "res/options.html", // 选项页面的路径
  "permissions": [ "tabs" , "webRequest", "webRequestBlocking", "http://*/*", "https://*/*", "contextMenus", "notifications"] // 扩展需要的权限
}

Chrome Extension简介

开始开发之前,我们先来刷一波基础知识。

Chrome官方明确规定了插件、扩展和应用的区别:

  • 插件(Plugin)是通过调用 Webkit 内核 NPAPI 来扩展内核功能的一种组件,工作在内核层面,理论上可以用任何一种生成本地二进制程序的语言开发,比如 C/C++、Java 等。插件重点在于接入浏览器底层,拥有更多的权限,可调用系统API,因此插件一般都不能跨系统。比如说最近Adobe宣布放弃的Flash,下载资源的迅雷以及网上付款的网银,它们都提供了Chrome插件,用以在特定场景启用并运行,从而实现丰富的功能。
  • 扩展(Extension)是通过调用 Chrome 提供的 Chrome API 来扩展浏览器功能的一种组件,它完全基于Chrome浏览器,借助HTML,CSS,JS等web技术实现功能,是Chrome提供的一种可开发的扩展技术。比如说今年横空出世的微信小程序,它就是微信提供的一种扩展技术。相对于插件而言,扩展程序拥有有限的权限和API,对底层系统不感知,从而具有良好的跨平台特性。注意插件和扩展都只有在Chrome启动后才会运行。
  • 应用(Application)同样是用于扩充Chrome浏览器功能。它与扩展的区别就在于,它拥有独立运行的用户界面,并且Chrome未启动时也能独立调用,就像一个独立的App一样。

不注意区分的话,我们讲到Chrome插件,往往指的就是以上三者之一。为了避免引起误解,本篇将严格区分概念,避免使用插件这种含糊的说法。

如何安装扩展

开发扩展,首先得从安装开始,从Chrome 21起,Chrome浏览器就增加了对扩展安装的限制,默认只允许从 Chrome Web Store (Chrome 网上应用店)安装扩展和应用,这意味着用户一般只能安装Chrome Web Store内的扩展和应用。

如果你拖动一个crx安装文件到Chrome浏览器的任何一个普通网页,将会出现如下提示。

点击 继续 按钮,则会在浏览器左上角弹出如下警告。

如果你恰好在Github上发现一个不错的Chrome扩展程序,而Chrome Web Store中没有。是不是就没有办法安装呢?当然不是的,Chrome浏览器还有三种其它的方式可以加载扩展程序。

  • 如果是扩展程序源码目录,点击 chrome://extensions/ 页面的 加载已解压的扩展程序 按钮就可以直接安装。

  • 如果是crx安装文件,直接拖动至 chrome://extensions/ 页面即可安装。安装过程如下所示:

    1) 拖放安装

​ 2)点击添加扩展程序

​ 3)添加好的扩展如下所示。

  • 启动Chrome时添加参数 --enable-easy-off-store-extension-install ,用以开启简单的扩展安装模式,然后就能像之前一样随意拖动crx文件到浏览器页面进行安装。

说到安装,自然有人会问,安装了某款扩展后,怎么查看该扩展的源码呢?Mac系统的用户请记住这个目录 ~/Library/Application Support/Google/Chrome/Default/Extensions/ (windows的扩展目录暂无)。

扩展打包和更新

另外,中间的 打包扩展程序 按钮用于将本地开发的扩展程序打包成crx包,首次打包还会生成秘钥文件(如IHeader.pem),如下所示。

打包好的扩展程序,可以发送给其他人安装,或发布到Chrome Web Store(开发者注册费用为5$)。

右边的 立即更新扩展程序 按钮则用于更新扩展。

扩展的基本组成

通常一个Chrome扩展包含如下资源或目录:

  • manifest.json入口配置文件(1个,位于根目录)
  • js文件(至少1个,位于根目录或子级目录)
  • 32px、64px、128px的方形icon各1个(位于根目录或子级目录)
  • _locales目录, 用于提供国际化支持(可选,位于根目录)
  • popup.html 弹出页面(可选,位于根目录或子级目录)
  • background.html 后台运行的页面,主要用于引入多个后台运行的js(可选,位于根目录或子级目录)
  • options.html 选项页面,用于扩展的设置(可选,位于根目录或子级目录)

为了方便管理,个人倾向于将HTML、JS、CSS,ICON等资源分类统一到同一个目录。

扩展的分类

从使用场景上看,Chrome扩展可分为以下三类:

1)Browser Action,浏览器扩展,可通过manifest.json中的 browser_action 属性设置,如下所示。

"browser_action": {
  "default_title": "Qrcode",
  "default_icon": "images/icon.png",
  "default_popup": "index.html" // 可选的
},

以上是URL生成二维码的Browser Action扩展,运行如下所示:

该类扩展特点:全局扩展,icon长期占据浏览器右上角工具栏,每个页面均可用。

2)Page Action,页面级扩展,可通过manifest.json中的 page_action 属性设置,如下所示。

"page_action": {
  "default_title": "IHeader",
  "default_icon": "res/images/lightning_default.png",
  "default_popup": "res/index.html" // 可选的
},

以上是本篇将要讲解的Page Action的扩展——IHeader,它被指定为所有页面可见,其icon状态切换如下所示。

该类扩展特点:不同页面可以拥有不同的状态和不同的icon,icon在指定的页面可见,可见时位于浏览器右上角工具栏。

由上可见,Browser Action与Page Action功能上非常相似,配置上各自的内部属性也完全一致,它们不仅可以配置点击时弹出的页面,同时还可以绑定点击事件,如下所示。

// 以下事件绑定一般在background.js中运行
// Browser Action
chrome.browserAction.onClicked.addListener(function(tab) {
  console.log(tab.id, tab.url);
  chrome.tabs.executeScript(tab.id, {file: 'content.js'});
});
// Page Action
chrome.pageAction.onClicked.addListener(function(tab) {
  console.log(tab.id, tab.url);
});

如果非要说两者的差别,开发中能够感受到的就是:前者不需要维护icon状态,后者需要针对每个启用的页面管理不同的icon状态。

3)Omnibox,全能工具条,可通过manifest.json中的 omnibox 属性设置,如下所示。

"omnibox": {
  "keyword": "mdn-" //URL地址栏输入关键字"mdn-"+空格后,就会触发Omnibox
},

以上是MDN网站快捷查询的Omnibox扩展,运行如下所示:

很明显,你可以对地址栏的各种输入做定制,Chrome的URL地址栏只所以强大,omnibox可谓功不可没。

该类扩展特点:运行在URL地址栏,无弹出界面,用户在输入时,扩展就可以显示建议或者自动完成一些工作。

以上三类决定了扩展如何在浏览器中运行。除此之外,每个扩展程序还可以任意搭载如下页面或脚本。

  • Background Page,后台页面,可通过manifest.json中的 background 属性设置,里面再细分 script 或 page ,分别表示脚本和页面,如下所示。

    "background": {
      "persistent": true, //默认为false,指定为true时将在后台持续运行
      "scripts": ["res/js/background.js"] // 指定后台运行的js
      // "page": ["res/background.html"]  // 指定后台运行的html,html中需引入若干个js,没有用户界面,实际上就相当于引入多个js脚本
    },

    Background Page在扩展中之所以重要,主要归功于 它可以使用所有的Chrome.* API 。借助它 popup.js 和 content.js 可以随时进行消息通信,并且调用它们原本无法调用的API。

    根据persistent值是否为true,Background Page可分为两类:① Persistent Background Pages ,② Event Pages 。前者持续运行,随时可访问;后者只有在事件触发时才能访问。

    该页面特点:运行在浏览器后台,无用户界面,后台页面可用于页面间消息通信以及后台监控,一旦浏览器启动,后台页面就会自动运行。

  • Content Script,内容脚本,可通过manifest.json中的 content_scripts 属性设置,如下所示。

    "content_scripts": [
      {
        "all_frames": true, // 默认为false,指定为true意味着frame中也加载内容脚本
        "matches": ["\u003Call_urls>"], // 匹配所有URL,意味着任何页面都会加载
        "js": ["res/js/content.js"], // 指定运行的内容脚本
        "run_at": "document_end" // 页面加载完成后执行
      }
    ],

    除了配置之外,内容脚本还可以通过js的方式动态载入。

    // 动态载入js文件
    chrome.tabs.executeScript(tabId, {file: 'res/js/content.js'});
    // 动态载入js语句
    chrome.tabs.executeScript(tabId, {code: 'alert("Hello Extension!")'});

    该脚本特点:每个页面在加载时都会加载内容脚本,加载时机可以指定为 document_start 、 idel 或 end (分别为页面DOM加载开始时,空闲时及完成后); 内容脚本是唯一可以访问页面DOM的脚本 ,通过它可以操作页面的DOM节点,从而影响视觉呈现;基于安全考虑,内容脚本被设计成与页面其他的JS存在于两个不同的沙盒,因此无法互相访问各自的全局变量。

  • Option Html,设置页面,可通过manifest.json中的 options_page 属性设置,如下所示。

    "options_page": "res/options.html",

    该页面特点:点击扩展程序icon的右键菜单上【选项】按钮进入到设置页面,该页面一般用于扩展的选项设置。

  • Override Html,替换新建标签页的空白页面,可通过manifest.json中的 chrome_url_overrides 属性设置,如下所示。

    "chrome_url_overrides":{
      "newtab": "blank.html"
    },

    该页面特点:常用于替换浏览器默认的空白标签页内容,多见于新开标签页时的壁纸程序,基于它你完全可以打造一个属于自己的空白页。

  • Devtool Page,开发者页面,可通过manifest.json中的 devtools_page 属性设置,如下所示。

    "devtools_page": "debug.html",

    该页面特点:随着控制台打开而启动,可用于将扩展收到的消息输出到当前控制台。

总之,对于Chrome扩展而言,Browser Action、Page Action 或 Omnibox之间是互斥的,其它情况下它并不限制你需要添加哪些页面或脚本,只要你愿意,就可以随意组合。

扩展如何运行调试

只要你会写js,就可以开发Chrome扩展程序了。涉及到开发,调试是不可避免的,Chrome扩展的调试也非常简单。我们都知道Chrome浏览器的 chrome://extensions/ 页面可以查看所有的Chrome扩展,不仅如此,该页面下的 加载已解压的扩展程序 按钮,便可以直接加载本地开发的扩展程序,如下所示。

小编推荐:欲学习电脑技术、系统维护、网络管理、编程开发和安全攻防等高端IT技术,请 点击这里 注册黑基账号,公开课频道价值万元IT培训教程免费学,让您少走弯路、事半功倍,好工作升职加薪!



免责声明:本文由投稿者转载自互联网,版权归原作者所有,文中所述不代表本站观点,若有侵权或转载等不当之处请联系我们处理,让我们一起为维护良好的互联网秩序而努力!联系方式见网站首页右下角。


鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论


新出炉

返回顶部