跳到主要内容

XHR 提交劫持与内容监听

本章将学会如何监听 XmlhttpRequest 的返回内容。

内容监听

这是我在国外网站找到的一个小例子

function addXMLRequestCallback(callback) {
var oldSend, i;
if (XMLHttpRequest.callbacks) {
XMLHttpRequest.callbacks.push(callback);
} else {
XMLHttpRequest.callbacks = [callback];
oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
for (i = 0; i < XMLHttpRequest.callbacks.length; i++) {
XMLHttpRequest.callbacks[i](this);
}
return oldSend.apply(this, arguments);
};
}
}
addXMLRequestCallback(function (xhr) {
xhr.addEventListener("load", function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseURL);
}
});
});

使用是通过调用 addXMLRequestCallback 传入我们的劫持函数来实现,我们的劫持函数会收到一个 xhr,可以对该 xhr 监听 load 事件,并等待readyState4,status200 的时候打印 xhr 的内容。

提示

readyState4status200 通常表示请求完毕并且响应正常

这里我们打印的是 xhr.responseURL,当然你也可以直接打印 xhr,然后查看需要查看哪些属性。

接下来我们来看 addXMLRequestCallback 的实现

//判断XMLHttpRequest对象下是否存在回调列表,存在就push一个回调的函数
if (XMLHttpRequest.callbacks) {
XMLHttpRequest.callbacks.push(callback);
} else {
//如果不存在则在xmlhttprequest函数下创建一个回调列表
//这部分代码只会执行一次,因为创建callbacks后,上面的if会为真,然后将函数push进callbacks。
XMLHttpRequest.callbacks = [callback];
oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
//循环回调xml内的回调函数
for (i = 0; i < XMLHttpRequest.callbacks.length; i++) {
XMLHttpRequest.callbacks[i](this);
}
//由于我们复写了send函数,所以当我们在调用原send的函数的时候,需要对其传入this引用
//而arguments是传入的参数列表,我们可以在这里实现一些提交参数的修改
return oldSend.apply(this, arguments);
};
}

那么我们到这里就学会了内容监听,接下来我们看看如何进行提交劫持

提交劫持

首先我们看一下 xhr 的例子

// 1. 创建 XHR 对象
var xhr = new XMLHttpRequest();
// 2. 调用 open 函数
xhr.open("POST", "http://www.xxx.com");
// 3. 调用 send 函数
xhr.send("a=1&b=2");
// 4. 监听 onreadystatechange 事件
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 获取服务器响应的数据
console.log(xhr.responseText);
}
};

可以看到发送数据主要在 send 中,我们应该主要对 send 进行劫持。

那么我们可以写出 hook 代码。

oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (...args) {
console.log("获取到了函数", args); //此处进行劫持
return oldSend.call(this, ...args);
};

如何在 send 中对 url 进行判断

在上方的例子中我们可以知道在 open 中传入的 url 地址,而在 send 中发送数据

我们如果在 send 中想要获取 url 地址的话,使用 this 来查看 xhr 实例是找不到地址的

这个时候我们可以对 open 也进行劫持,将数据挂载到 xhr 实例上,然后再在 send 的时候进行读取

let oldOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._url = url;
return oldOpen.call(this, method, url, async, user, password);
};
let oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
console.log("url", this._url);
return oldSend.apply(this, arguments);
};

本节对应实战

实战百度自定义分享码