zoukankan      html  css  js  c++  java
  • 161129、详解5种跨域方式及其原理

    同源定义 
    如果两个页面拥有相同的协议(protocol),端口(如果指定),和主机,那么这两个页面就属于同一个源(origin)。

    以下是同源检测的示例

    URL结果原因
    http://store.company.com/dir2/other.html Success  
    http://store.company.com/dir/inner/another.html Success  
    https://store.company.com/secure.html Failure 协议不同
    http://store.company.com:81/dir/etc.html Failure 端口不同
    http://news.company.com/dir/other.html Failure 主机不同

    1.jsonp

    script标签是不受同源策略影响的,它可以引入来自任何地方的js文件。 
    而jsonp的原理就是,在客户端和服务端定义一个函数,当客户端发起一个请求时,服务端返回一段javascript代码,其中调用了在客户端定义的函数,并将相应的数据作为参数传入该函数。

    function jsonp_cb(data) {

       console.log(data);

    }

    function ajax(){

       var url = "http://xx.com/test.php?jsonp_callback=jsonp_cb";

       var script = document.createElement('script');

       // 发送请求

       script.src = url;

       document.head.appendChild(script);

    }

    ajax()

    服务端获取到jsonp_callback传递的函数名jsonp_cb,返回一段对该函数调用的js代码

    jsonp_cb({

      "name": "story"

    });

    2.img ping

    img标签也是没有跨域限制的,但它只能用来发送GET请求,且无法获取服务端的响应文本,可以利用它实现一些简单的、单向的跨域通信,例如跟踪用户的点击

    var img = new Image();

    img.onload = function(){

       console.log('done')

       img.onload = null;

       img = null;

    }

    img.src = "http://xx/xx.gif"

    3.window.name

    window对象拥有name属性,它有一个特点:相同协议下,在一个页面中,不随URL的改变而改变 
    示例代码

    window.name = 'string' // 字符串,一般允许的最大值为2M

    console.log(window.name)

    location = 'http://funteas.com/'

    此时,在控制台输入window.name,结果依然是”string”

    window.name // "string"

    window.name的值只能是字符串,任何其他类型的值都会“转化”为字符串 
    例如

    window.name = function(){}

    console.log(window.name)

    // "function(){}"

    通过window.name实现跨域也很简单,iframe拥有contentWindow属性,其指向该iframe的window对象的引用,如果在iframe的src指向的页面中设置window.name值,那么就可以通过iframe.contentWindow.name就可以拿到这个值了

    var url = "http://funteas.com/lab/windowName";

    var iframe = document.createElement('iframe')

    iframe.onload = function(){

       var data = iframe.contentWindow.name

       console.log(data)

    }

    iframe.src = url

    document.body.appendChild(iframe)

    然而,chrome会提示你跨域了! 
    而我们已经知道window.name不随URL的改变而改版,也就是说,onload时,已经获取到了name,只不过因为不同源,当前页面的脚本无法拿到iframe.contentWindow.name,此时只需要把iframe.src改为同源即可

    var url = "http://funteas.com/lab/windowName";

    var iframe = document.createElement('iframe')

    iframe.onload = function(){

       iframe.src = 'favicon.ico';

       var data = iframe.contentWindow.name

       console.log(data)

    }

    iframe.src = url

    document.body.appendChild(iframe)

    刷新页面,你会发现iframe不断刷新,这是因为每次onload,iframe的src被修改,然后再次触发onload,从而导致iframe循环刷新,修改下即可

    var url = "http://funteas.com/lab/windowName";

    var iframe = document.createElement('iframe')

    var state = true;

    iframe.onload = function(){

       if(state === true){

           iframe.src = 'favicon.ico';

           state = false;

       }else if(state === false){

           state = null

           var data = iframe.contentWindow.name

           console.log(data)

       }

    }

    iframe.src = url

    document.body.appendChild(iframe)

    上面请求的是一个静态页面,而服务端通常需要的是动态数据

    echo '<script> window.name = "{"name":"story"}"</script>';

    4.postMessage

    postMessage允许不同源之间的脚本进行通信,用法

    otherWindow.postMessage(message, targetOrigin);

    • otherWindow 引用窗口 iframe.contentwindow 或 window.open返回的对象

    • message 为要传递的数据

    • targetOrigin 为目标源

    // http://127.0.0.1:80

    var iframe = document.createElement('iframe')

    iframe.onload = function(){

       var popup = iframe.contentWindow

       popup.postMessage("hello", "http://127.0.0.1:5000");

    }

    iframe.src = 'http://127.0.0.1:5000/lab/postMessage'

    document.body.appendChild(iframe)

    // 监听返回的postMessage

    window.addEventListener("message", function(event){

       if (event.origin !== "http://127.0.0.1:5000") return;

       console.log(event.data)

    }, false)

    // http://127.0.0.1:5000/lab/postMessage

    window.addEventListener("message", function(event){

       // 验证消息来源

       if (event.origin !== "http://127.0.0.1") return;

       console.log(event.source); // 消息源 popup

       console.log(event.origin); // 消息源URI https://secure.example.net

       console.log(event.data); // 来自消息源的数据 hello

       // 返回数据

       var message = 'world';

       event.source.postMessage(message, event.origin);

    }, false);

    5.CORS

    CORS(跨域资源共享)是一种跨域访问的机制,可以让AJAX实现跨域访问。它允许一个域上的脚本向另一个域提交跨域 AJAX 请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。

    Access-Control-Allow-Origin: * // 允许来自任何域的请求

    Access-Control-Allow-Origin: http://funteas.com/ // 仅允许来自http://funteas.com/的请求

    当客户端的ajax请求的url为其他域时,对于支持CORS的浏览器,请求头会自动添加Origin,值为当前host

    var xhr = new XMLHttpRequest();

    var url = 'http://bar.other/resources/public-data/';

    xhr.open('GET', url, true);

    xhr.send();

    CORS默认不发送cookie,如果要发送cookie,需要设置withCredentials

    var xhr = new XMLHttpRequest();

    xhr.withCredentials = true;

    同时,服务端也要设置

    Access-Control-Allow-Credentials: true

    查看MDN关于CORS的介绍【https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS】

    以上是我使用过的跨域方式,其他方式诸如document.domain、flash等,因为没有用过,就不再多作介绍,有兴趣的同学可以自己了解下。

  • 相关阅读:
    数据类型装换
    变量及数据类型
    27 网络通信协议 udp tcp
    26 socket简单操作
    26 socket简单操作
    14 内置函数 递归 二分法查找
    15 装饰器 开闭原则 代参装饰器 多个装饰器同一函数应用
    12 生成器和生成器函数以及各种推导式
    13 内置函数 匿名函数 eval,exec,compile
    10 函数进阶 动态传参 作用域和名称空间 函数的嵌套 全局变量
  • 原文地址:https://www.cnblogs.com/zrbfree/p/6165064.html
Copyright © 2011-2022 走看看