跨域以及解决方案汇总

跨域

是指一个域下面的文档或者脚本视图去请求另一个域下的资源

  1. 资源跳转:A链接,重定向,表单提交
  2. 资源嵌入: ,
    $.ajax("http://www.b.com/request", {
      jsonpCallback: "moty",
      dataType: "jsonp",
      success: function(json) {...}
    });
    
  3. document.domain + iframe 跨域

    a. 这两个域名必须属于同一个一级域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域。

    b. 如果是同一级域名下的子域名,如:m.a.com, api.a.com, 需要在 javascript 里面设置 domain 才行

    document.domain = 'a.com';
    

    news.baidu.com下的news.html页面:

    
    

    map.baidu.com下的map.html页面:

      我是map.baidu.com中的ul
  4. location.hash + iframe

    原理是利用location.hash来进行传值。

    在url: http://a.com#helloword中的‘#helloworld’就是location.hash,改变hash并不会导致页面刷新,所以可以利用hash值来进行数据传递,当然数据容量是有限的。

    假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息,cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面,这时的hash值可以做参数传递用。

    cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据(由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe;Firefox可以修改)。

    同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一点有变化则获取获取hash值。

    a.html下的文件cs1.html

    function startRequest(){
        var ifr = document.createElement('iframe');
        ifr.style.display = 'none';
        ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
        document.body.appendChild(ifr);
    }
     
    function checkHash() {
        try {
            var data = location.hash ? location.hash.substring(1) : '';
            if (console.log) {
                console.log('Now the data is '+data);
            }
        } catch(e) {};
    }
    setInterval(checkHash, 2000);
    

    cnblogs.com下的cs2.html

    //模拟一个简单的参数处理操作
    switch(location.hash){
        case '#paramdo':
            callBack();
            break;
        case '#paramset':
            //do something……
            break;
    }
     
    function callBack(){
        try {
            parent.location.hash = 'somedata';
        } catch (e) {
            // ie、chrome的安全机制无法修改parent.location.hash,
            // 所以要利用一个中间的cnblogs域下的代理iframe
            var ifrproxy = document.createElement('iframe');
            ifrproxy.style.display = 'none';
             // 注意该文件在"a.com"域下
            ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata';   
            document.body.appendChild(ifrproxy);
        }
    }
    

    a.html下的文件cs3.html

    //因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
    parent.parent.location.hash = self.location.hash.substring(1);
    
  5. window.name + iframe

    window.name (一般在js代码里出现)的值不是一个普通的全局变量,而是当前窗口的名字,这里要注意的是每个iframe都有包裹它的window,而这个window是top window的子窗口,而它自然也有window.name的属性, window.name属性的神奇之处在于name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。

    window.name = 'abc';
    window.name; // abc
    window.location = 'http://www.baidu.com';
    window.name; // abc
    

    a.html, proxy.html 同属于一个域下,b.html是另一个域的

    b.html内容

    
    

    a.html

    
    

    可以看到, 第一次设置iframe的地址为b.html, 这样的话b.html会被加载进来,但是并不能直接访问iframe.contentWindow.name, 因为a.html和b.html目前不同源,如果将loadfn的实现改为var data = iframe.contentWindow.name;,会出来这个错误:

    a.html: 
    Uncaught DOMException: Blocked a frame with origin "http://localhost:8001" from accessing a cross-origin frame.
    

    那怎么办呢, 既然不同源, 就改成同源呗, 所以将iframe地址改成与a.html同源的proxy.html,由于window.name在地址变化时值不变, 所以iframe.contentWindow.name的值还是之前的值, 也就是b.html窗口的值, 而又满足的同源的要求, 所以可以访问成功。

  6. postMessage

    window.postMessage的功能是允许程序员跨域在两个窗口/frames间发送数据信息。基本上,它就像是跨域的AJAX,但不是浏览器跟服务器之间交互,而是在两个客户端之间通信。安全着想,接受消息的时候应该检验来源,也就是event.origin或者event.source。

    a.html

    page A

    
    b.html
    
    ```html
    

    page B

    1. 跨域资源共享(CORS)

      CORS 通过新增一系列的HTTP头,让服务器能声明那些来源能访问该服务器上的资源,GET以外的请求,会以OPTIONS请求方式发一个预请求,从而得知服务器对资源请求支持的HTTP方法,在确认服务器允许跨域请求资源的情况下,以实际的HTTP请求方法发送真正的请求。

      1. 请求头:

        Origin :

        普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域。

        Access-Control-Request-Method :

        接下来请求的方法,例如PUT, DELETE等等

        Access-Control-Request-Headers :

        自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中

      2. http响应头

        然后浏览器再根据服务器的返回值判断是否发送非简单请求。简单请求前面讲过是直接发送,只是多加一个origin字段表明跨域请求的来源。然后服务器处理完请求之后,会再返回结果中加上如下控制字段

        Access-Control-Allow-Origin :

        允许跨域访问的域,可以是一个域的列表,也可以是通配符"*"。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://foo.example/subdir/ 是无效的。但是不同子域名需要分开设置,这里的规则可以参照同源策略

        Access-Control-Allow-Credentials :

        是否允许请求带有验证信息,XMLHttpRequest请求的withCredentials标志设置为true时,认证通过,浏览器才将数据给脚本程序。

        Access-Control-Expose-Headers :

        允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息

        Access-Control-Max-Age :

        缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数

        Access-Control-Allow-Methods :

        允许使用的请求方法,以逗号隔开

        Access-Control-Allow-Headers :

        自定义的头部,以逗号隔开,大小写不敏感

    2. nginx代理跨域

    3. nodejs中间件代理跨域

    4. websocket协议跨域

你可能感兴趣的:(跨域以及解决方案汇总)