Ajax-hook之百度图片测试

发布时间 2023-06-13 14:19:36作者: 守护式等待
/*
Ajax-hook是一种比较实用的技术,它可以自定义XMLHttpRequest中的方法和属性,
覆盖全局的XMLHttpRequest。当网站在使用Ajax请求动态渲染时,开发者可以对其中的
请求参数和响应数据进行任意修改和展示,有些类似前面讲过的本地覆盖,区别是这里
会去调用原本的XMLHttpRequest。
*/
//2.4.1 Ajax-hook源码分析
!function (ob){
     ob.hookAjax=function(funs){
     window._ahrealxhr=window._ahrealxhr||XMLHttpRequest;
     XMLHttpRequest=function(){
        /*
        查看Ajax-hook的核心代码,发现它会将浏览器window对象传入,并先获取全局的真实XMLHttpRequest,
        同时对XMLHttpRequest对象中的API接口进行遍历,其中的方法和属性会采取不同的策略进行处理。
        如果遍历到的API接口类型是function,就会用hookfun方法处理,
        属性会用Object.defineProperty方法进行定义,代码如下:
        */
            this.xhr=new window._ahrealxhr;
            for (var attr in this.xhr) {//遍历XHR接口(可枚举)
                var type="";
                try{
                        type=typeof this.xhr[attr]//获取接口类型
                    }catch(e){}
                     if (type==="function"){      //方法
                        this[attr]=hookfun(attr);  
                    }else{                        //属性
                         Object.defineProperty(this,attr,{
                         //get:在获取定义的属性时,会调用这里定义的函数。它不传入任何参数,但是会传入this对象。
                         //该函数的返回值会被用作属性的值。
                         get:getFactory(attr),
                         //set:在定义的属性被修改时,会调用这里定义的函数。该方法会接受被定义的新值作为参数,
                         //同时也会传入this对象。
                         set:setFactory(attr)
                    })
                }
            }
        };
    /*
    Ajax-hook获取属性时调用getFactory函数,返回值使用了三元运算符。this.hasOwnProperty方法会返回一个布尔值,
    用来表明this对象自身属性中是否具有指定的属性。
    */
    function getFactory(attr){
        return function(){
                 //判断this对象自身属性中是否具有指定的属性
           return this.hasOwnProperty(attr+"_")?this[attr+"_"]:this.xhr[attr];
                                                //这里通常返回以on开头的属性对应的,用户自定义的方法调用
       }
    }
    /*
    Ajax-hook运行周期内,修改属性会调用setFactory函数,它会接受被定义的新值作为参数,这里用f来指代。
    如果属性以on开头,就会在开发者自定义的func中进行匹配并调用,开发者自定义的水星返回为true表示进行阻断,
    自定义的函数执行完毕后不会再执行XHR原生函数,反之会继续执行XHR原先的回调。
    */
    function setFactory(attr){
       return function(f){
            var xhr=this.xhr;
            var that=this;
            if (attr.indexOf("on")!=0){
                //Ajax-hook在遍历的属性后加上了下划线,在setFactory中对不以on开头的属性会进行下划线添加的新映射值
                this[attr+"_"]=f;
                return;
            }
            if (funs[attr]){
                xhr[attr]=function(){
                    funs[attr](that)||f.apply(xhr,arguments);
             }
          }else{
             xhr[attr]=f;
          }
        }
    }
    //在对XMLHttpRequest中原生的方法进行处理时,会使用如下hookfun方法。
    /*
    它首先会把当前遍历来的方法中的参数赋值到args中,之后会获取开发者自定义的XMLHttpRequest方法,
    如果能够获取到开发者自定义的方法,就会对当前方法进行覆盖,并将参数传入调用:如果没有获取到,
    说明开发者没有重写这个方法,会继续调用原生方法。
    处理XMLHttpRequest属性时,主要使用Object.defineProperty,这个方法的作用是直接在一个对象上
    定义一个新的属性,或者修改一个已经存在的属性,代码如下:
    Object.defineProperty(obj,prop,descriptor)
    */
    function hookfun(fun){
        return function(){
            var args=[].slice.call(arguments);
            if (funs[fun] && funs[fun].call(this,args,this.xhr)){        
                  return;          
            }
            return this.xhr[fun].apply(this.xhr,args);
        }
    }
    return window._ahrealxhr;
}
ob.unHookAjax=function(){
    if (window._ahrealxhr) XMLHttpRequest=window._ahrealxhr;
       window._ahrealxhr=undefined;
    }
}(window);

//2.4.2 Ajax-hook拦截
/*
在使用Ajax技术的网页上,使用Ajax-hook进行代码调式会让开发者处理起来游刃有余。
XMLHttpRequest中的方法和属性都可以进行自由操作,所以能做的事也非常多,如可以
直接对网页的Ajax请求中的参数进行打印,也可以在发送Ajax请求的时候进行断点调式,
或直接将返回的响应数据进行展示,代码如下:
*/
hookAjax({
    //检测到状态码变化,打印输出响应体
    onreadystatechange:function(xhr){
        if(xhr.status==200){
            console.log("response:\n",xhr.response)
            //console.log("response")
        }
        return false;//不进行阻断
    },
    //在打开Ajax请求的时候进行操作
    open:function(arg){
         console.log("open called:\nmethod:%s,\nurl:%s,\nasync:%s\n",arg[0],arg[1],arg[2])
         //console.log("open")
    },
    //在发送Ajax请求的时候进行操作
    send:function(arg){
        debugger;//设置断点
        console.log("send called:",arg)
    },
    //在设置请求头部的时候进行操作
    setRequestHeader:function(arg){
        console.log("setRequestHearder:",arg)
    },
})
/*
百度图片(https://image.baidu.com/)的加载使用了Ajax技术,可用来做测试。将以上hook代码注入到网页中,
或直接输入Console控制中,查看Console面板的输出:
Console面板会直接输出请求图片的Ajax接口,并且打印设置的headers头部,
还展示了响应体返回的JSON图片数据。
*/