日期:2014-05-16  浏览次数:20406 次

使用 JavaScript 拦截和跟踪浏览器中的 HTTP 请求

HTTP 请求的拦截技术可以广泛地应用在反向代理、拦截 Ajax 通信、网页的在线翻译、网站改版重构等方面。而拦截根据位置可以分为服务器端和客户端两大类,客户端拦截借助 JavaScript 脚本技术可以方便地和浏览器的解释器及用户的操作进行交互,能够实现一些服务器端拦截不容易实现的功能。本文将重点介绍通过 JavaScript 脚本在客户端对页面内容进行拦截修改的一些原理和技术。

HTTP 请求的拦截功能的应用

随着互联网应用及 B/S 架构的软件系统的风行,越来越多的基于浏览器的应用得以开发和部署。对已经上线的应用系统进行拦截和跟踪可以方便、快捷地实现很多功能。例如,

?? 1. IBM 的 Tivoli Access Manager 单点登录(SSO,关于更多 TAM 的信息,请参见本文后附的资源列表)就是基于逆向代理服务器的原理实现的,逆向代理服务器的实现必须要对既有页面中的一些 URL 进行拦截和改写;
?? 2. 现今一些有趣的匿名代理服务器也是根据逆向代理服务器的原理实现的(如 http://www.youhide.com/),这类网站也有 URL 改写的需求;
?? 3. 另外,IBM 提供的在线翻译网页的服务中,为了能够让被翻译页面的链接所指向的页面在用户交互时继续得到翻译,我们同样需要使用 URL 的拦截和改写功能。

不仅仅是 URL 改写,通过拦截和跟踪技术可以在极小修改或者使用反向代理不修改原网页的前提下为基于 BS 的 Web 应用提供更复杂的页面改写,脚本改写等功能。

基本原理

如图 1 所示,传统的 Web 访问模型通过浏览器向 HTTP 服务器获取 Web 数据,浏览器将获取的数据进行解释渲染,得到用户的客户端界面。

图 1. 传统 Web 访问模型


图 1. 传统 Web 访问模型

而在带有服务器端和浏览器端跟踪和拦截的访问模型中,传统的访问方式发生了变化。服务器端和浏览器端的跟踪和拦截将页面中的拦截对象,在适当的拦截时机进 行了拦截变化,导致用户面前的浏览器的解释渲染结果和 HTTP 服务器上的内容存在了逻辑上的差异,而这种差异恰巧是 HTTP Server 所需要的结果,而不方便将差异部署在 HTTP Server 上。 Server trace 可以作为反向代理服务器的一项逻辑功能存在,Browser trace 是通过脚本完成在客户端拦截和跟踪行为。

服务器端实现和客户端浏览器实现的比较

拦截根据位置可以分为服务器端和客户端两大类,客户端拦截借助 JavaScript 脚本技术可以方便地和浏览器的解释器和用户的操作进行交互,能够实现一些服务器端拦截不容易实现的功能。如果将服务器端和客户端拦截融合在一起,可以很好地处理拦截和跟踪问题。

表 1. 服务器端拦截和客户端拦截的功能比较

功能比较服务器端拦截客户端拦截
向页面头部插入代码 强。简洁,无需逐个加入代码 麻烦。需要逐个为页面加代码
访问资源的权限 强。可以访问跨域资源。 受限。浏览器间有差异
会话的控制和访问 强。可以使用相应 API 。 受限。需要使用 cookie
页面 HTML 的拦截和跟踪 一般。通过正则跟踪。 强。可以使用 DOM 操作
页面脚本的拦截和跟踪 一般。通过正则跟踪 强。可以利用解释器环境
跟踪用户的交互行为 一般。通过正则跟踪 强。可以利用浏览器事件


从表 1 可以看出服务器端拦截和客户端拦截具有很强的互补性,服务器端拦截擅长服务器 - 浏览器的通信控制、资源的访问以及批量页面的操作,浏览器端拦截可以依靠 Script 解释器的环境,擅长个体页面的逻辑拦截和跟踪。本文因篇幅限制,主要针对使用 JavaScript 的浏览器端的拦截和跟踪进行介绍。不过在进入具体的实现之前,先介绍几个术语。?

拦截对象

拦截对象指的是需要被处理的元素,可能包括 [HTML 标签 ],[HTML 标签的属性 ],[ 脚本变量 ],[ 对象 ],[ 函数 ] 等。拦截对象 和 拦截时机 就构成了一个个非常实际的功能或用途。

功能 :拦截页面中的所有链接,将其 href 属性的值 xxx 改为 http://proxyhost/agent?url=xxx

拦截对象 : <A> 标签

拦截时机:根据需要适当选择

功能 :拦截页面中的 document.write 的功能,在调用此函数前修改函数参数

拦截对象 : 对象方法

拦截时机:根据需要适当选择

功能 : 拦截页面中用户自定义的函数 functionA 的调用

拦截对象: 用户自定义函数

拦截时机 : 根据需要适当选择

插入点和拦截时机

插入点: 插入点指的是拦截脚本 (JavaScript) 植入页面文件的位置。
拦截时机: 拦截时机从页面的载入过程可以分为 [ 页面加载前 ],[ 页面加载中 ],[ 页面加载后 ] 。
通常来说,在页面加载后进行处理代码相对简单,但是功能也相对受限。页面加载前处理代码实现相对复杂,但是功能相对丰富。用户可以根据需要混合使用。
从用户的角度出发,自然希望插入点简单,拦截时机和拦截点丰富。 因此本文的讨论的拦截和跟踪的技术的出发点也就明了了,即 : 在尽可能少的对原有系统的改变的前提下,通过 JavaScript 代码跟踪并改变系统的行为与内容。

拦截时机的实现

拦截时机中讲到的 [ 页面加载前 ] 是指在用户的 page 内容被浏览器解释前进行拦截的时机。这种拦截可以在用户没有看到渲染结果的时候完成拦截修改,这种技术将在第五种方法中详细介绍。

[ 页面加载中 ] 拦截通常是将代码放在页面中合适的位置,实现代码简单,但是代码的插入点 ( 也就是页面的合适位置 ) 可能阻碍了这种时机的使用频度,也违背了本文的初衷。因此 [ 页面加载中 ] 的拦截时机通常使用 [ 页面加载前 ] 的拦截时机进行替代。

例如 : 某 Web 页面的第 100 行有如下内容:

  1. <script> function ?userFunction()?{? //do?some?actions?}</script> ??

在页面的第 200 行调用了上面定义的函数,如:

  1. <script>userFunction();?<script>??

第 200 行的脚本会在页面的渲染过程中发生作用,如果希望拦截的话,就需要在第 100 行后,200 行前对 userFunction 进行覆盖,这样就会在页面的加载过程中完成拦截。这种做法严重依赖代码的插入点,因此使用效果不好。

[ 页面加载后 ] 拦截,首先在这里介绍,实现简单,能实现很多功能。

方法 : 通常在页面的尾部 </body> 标签前,加入: