本帖最后由 cxxjackie 于 2024-6-14 23:14 编辑
一个ajax劫持库,支持xhr和fetch劫持。注意:劫持发生的时机是库引入的时候,因此脚本应运行于document-start阶段,或至少于目标请求发生之前。不同版本的ajaxHooker同时生效时可能发生冲突,相同版本则不会引发错误,但修改效果可能相互覆盖。因1.4.0合并了不同脚本的ajaxHooker实例,与之前所有版本均不兼容,请尽量引用最新版本的库,以避免与其他脚本发生冲突。
符合Greasy Fork规则的引用地址:
// @require https://scriptcat.org/lib/637/1.4.3/ajaxHooker.js#sha256=y1sWy1M/U5JP1tlAY5e80monDp27fF+GMRLsOiIrSUY=
ajaxHooker.hook
核心方法,通过一个回调函数进行劫持,每次请求发生时自动调用回调函数。可以将所有劫持放在同一回调函数中,也可以多次调用hook方法。示例:
ajaxHooker.hook(request => {
console.log(request);
});
参数request是一个对象,其包含以下属性:
type
只读属性。一个字符串,表明请求类型是xhr还是fetch。
async
只读属性。异步请求为true,同步请求为false,异步特性无法作用于同步请求。
url
method
请求的url和method,可以直接修改。
abort
是否取消请求,设置为true即可取消本次请求。
headers
请求头,可以直接修改。
data
请求携带的数据,可以直接修改。
response
响应内容,必须通过一个回调函数进行读取和修改。响应内容为一个对象,包含finalUrl、status、responseHeaders和被读取的响应数据,除响应数据可修改,其他属性是只读的。响应数据是哪个属性取决于哪个属性被读取,xhr可能的属性为response
、responseText
、responseXML
,fetch可能的属性为arrayBuffer
、blob
、formData
、json
、text
。修改对应属性即可影响读取结果,进而实现响应数据的修改。示例:
ajaxHooker.hook(request => {
if (request.url === 'https://www.example.com/') {
request.response = res => {
console.log(res);
res.responseText += 'test';
};
}
});
当abort
设置为true且response
回调函数存在时,库将取消原请求并伪造一个成功响应,此时响应数据为空,直接对其赋值即可伪造响应结果。当你不需要原响应值时可使用此特性,以提高响应速度,减少不必要的请求。示例:
ajaxHooker.hook(request => {
if (request.url === 'https://www.example.com/') {
resquest.abort = true;
request.response = res => {
// res的finalUrl、status、responseHeaders均是伪造的,其他属性不存在
console.log(res);
res.responseText = 'test';
};
}
});
以下情况发生时,response
回调函数将不会被执行:
1.请求未abort且发生失败时。
2.另一个脚本引入ajaxHooker且同时修改了response,则当前回调函数可能被覆盖(取决于执行顺序)。
异步特性
注意:异步特性无法作用于同步请求,但同步修改仍然有效。
你可以将以上所有可修改属性赋值为Promise,原请求将被阻塞直至Promise完成(若发生reject,数据将不会被修改),此特性可用于异步劫持。以下是一个异步修改响应数据的例子:
ajaxHooker.hook(request => {
request.response = res => {
const responseText = res.responseText; // 注意保存原数据
res.responseText = new Promise(resolve => {
setTimeout(() => {
resolve(responseText + 'test');
}, 3000);
});
};
});
也可以传入async回调函数以实现异步:
ajaxHooker.hook(async request => {
request.data = await modifyData(request.data);
request.response = async res => {
res.responseText = await modifyResponse(res.responseText);
};
});
ajaxHooker.filter
应于hook方法之前执行,此方法若尽早执行,有助于提升性能。
为hook方法设置过滤规则,只有符合规则的请求才会触发hook。过滤规则是一个对象数组,参考下例:
ajaxHooker.filter([
{type: 'xhr', url: 'www.example.com', method: 'GET', async: true},
{url: /^http/},
]);
type
可选,应是xhr或fetch。
url
可选,字符串或正则表达式,无需完全匹配。
method
可选,不区分大小写。
async
可选,布尔值。
ajaxHooker.protect
如果库劫持失败,可能是其他代码对xhr/fetch进行了二次劫持,protect方法会尝试阻止xhr和fetch被改写。应于document-start阶段尽早执行,部分网页下可能引发错误,谨慎使用。示例:
ajaxHooker.protect();
ajaxHooker.unhook
将xhr和fetch恢复至劫持前的状态,调用此方法后,hook方法不再生效。示例:
ajaxHooker.unhook();
更新日志
1.0.0
初始版本
1.0.1
修复应用于阿里云盘时的一个bug。
1.0.2
对响应头中的重复字段做合并处理。
1.1.0
1.headers和data属性现在可以直接读取修改了,回调函数方式已废弃,不向下兼容。
2.修复因原请求多次open和send引发的bug,减少多个ajaxHooker实例运行时的冲突现象。
1.1.1
处理多个ajaxHooker实例运行时的请求头冲突问题。
1.2.0
1.新增异步特性。
2.优化错误处理。
1.2.1
1.新增async函数支持。
2.优化xhr的劫持逻辑,以减少冲突概率。
1.2.2
1.新增filter方法。
2.修复已知问题。
1.2.3
1.修复filter方法的一个bug。
2.优化引用类型的响应数据读取问题。
1.2.4
1.减少对document-start的依赖。
2.现在可以正确处理URL类型的链接了。
1.3.0
1.重构部分代码,将xhr劫持改为Proxy方式。
2.修复已知问题。
1.3.1
修复了fetch请求的参数为Request类型时的一个bug。
1.3.2
处理异步劫持作用于同步请求时的bug,新增一个async参数。
1.3.3
修复一个小bug。
1.3.4
修复xhr请求可能意外变成同步的问题。
1.4.0
1.重构,减少重复代码(应该没有bug...吧)。
2.对不同脚本引入的ajaxHooker实例做合并处理,以减少冲突,提高性能。
3.现在允许通过abort参数在不发生请求的情况下伪造响应值。
4.修复已知问题。
1.4.1
修复特殊情况下有部分请求被跳过的问题。
1.4.2
修复了fetch请求的参数为Request类型时body类型不正确的bug。
1.4.3
1.修复特殊情况下有部分请求头丢失的问题。
2.xhr事件增加currentTarget劫持。