zoukankan      html  css  js  c++  java
  • JSONP && CORS

      前天面试被问到了跨域的问题,自我感觉回答的并不理想,下面我就分享一下整理后的总结分享给大家

    一、为什么要跨域

      安全限制

      JavaScript或Cookie只能访问同域下的内容——同源策略

      同源策略

      下表相对于: http://h5.jd.com/dir/ajax.js

      

      注意

    • 协议和端口造成的跨域问题,非前端解决范畴
    • 所谓域,是通过“url首部”来识别,而非判断域与ip的对应关系

      (“URL的首部”指window.location.protocol +window.location.host)

    二、跨域方案

    1. jsonp

      原理

      HTML里面所有带src属性的标签都可以跨域,如iframe,img,script等。

      所以可以把需要跨域的请求改成用script脚本加载即可,服务器返回执行字符串,但是这个字符串是在window全局作用域下执行的,你需要把他返回到你的代码的作用域内,这里就需要临时创建一个全局的回调函数,并把到传到后台,最后再整合实际要请求的数组,返回给前端,让浏览器直接调用,用回调的形式回到你的原代码流程中。

    • 首先,利用 script 标签的 src 属性实现跨域
    • 通过将前端方法作为参数传递到服务器端,然后由服务器注入参数之后再返回,实现服务器端向客户端通信
    • 由于使用script 标签的src 属性,因此只支持 get 方法

      一个简单的jsonp实现,其实就是拼接url,然后将动态添加一个script元素到头部

    1. 设定一个script标签
    <script src="http://jsonp.js?callback=xxx"></script>
    2. 服务器
    $callback = !empty($_GET['callback'] ? $_GET['callback'] : 'callback');
    echo $callback.'(.json_encode($data).)';

      详见博客 JSON 和 JSONP两兄弟

    2. cors

    方案对比
      JSONP CORS
    目的 跨域 跨域
    支持

    get

    (受IE下url长度不能超过2083个字节的限制和出于安全考虑,一般不用来提交数据)

    所有类型的http请求
    支持度 包括老式浏览器 不支持部分浏览器,移动端支持很好
    缺点

    1)安全问题(请求代码中可能存在安全隐患)

    2)确定jsonp请求是否失败不太容易

    3)只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题

    支持率
    原理

    被包含在一个回调函数中的JSON

    核心则是动态添加<script>标签来调用服务器提供的js脚本

    (允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了)

    使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败

    (只需由服务器发送一个响应标头即可)

    CORS需要浏览器和服务器同时支持

    实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨域通信


    1)两种请求方式

      简单请求、非简单请求

      a)简单请求:

      

      

      跨域时,浏览器自动在头部信息中添加一个origin 字段(指定请求源-协议+域名+端口),如下图所示

      

      服务器判断origin在域名许可范围内,返回响应:

      

      若不存在 Access-Control-Allow-Origin 字段,则出错

      以上头部信息中,CORS相关字段有

    • Access-Control-Allow-Origin
      必须字段,其值为 origin / *(可接受任意域名请求)
    • Access-Control-Allow-Credentials
      可选,是否允许发送Cookie
    • Access-Control-Expose-Headers
      可选,是否需要Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma之外的字段

      withCredentials 属性

       CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段

    Access-Control-Allow-Credentials: true

      另一方面,开发者必须在AJAX请求中打开withCredentials属性。

    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;

      注意

      如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

      b)非简单请求(不同时满足以上条件)

      请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

       浏览器对于非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息

      

      

      除了Origin字段,"预检"请求的头信息包括两个特殊字段。

    • Access-Control-Request-Method

       该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT

    • Access-Control-Request-Headers

       该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

    2.CORS 支持度

     

    3. document.domain

    两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

    举例来说,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie

    document.domain = 'example.com';

    现在,A网页通过脚本设置一个 Cookie

    document.cookie = "test1=hello";

    B网页就可以读到这个 Cookie。

    var allCookie = document.cookie;

    注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,规避同源政策,而要使用下文介绍的PostMessage API。

    4. iframe

     (只有在主域相同时才能使用)

      1)www.a.com/a.html中:

    document.domain = 'a.com';
    var ifr = document.createElement('iframe');
    ifr.src = 'http://www.script.a.com/b.html';
    ifr.display = none;
    document.body.appendChild(ifr);
    ifr.onload = function(){
        var doc = ifr.contentDocument || ifr.contentWindow.document;
        //在这里操作doc,也就是b.html
        ifr.onload = null;
    };

      2) 在www.script.a.com/b.html中:

    document.domain = 'a.com';

    5. window.postMessage:

    该方法是 HTML5 新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

    window.postMessage的功能是允许程序员跨域在两个窗口/frames间发送数据信息。基本上,它就像是跨域的AJAX,但不是浏览器跟服务器之间交互,而是在两个客户端之间通信

    postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

    子窗口向父窗口发送消息的写法类似。

    window.opener.postMessage('Nice to see you', 'http://aaa.com');

    父窗口和子窗口都可以通过message事件,监听对方的消息。

    window.addEventListener('message', function(e) {
      console.log(e.data);
    },false);

    message事件的事件对象event,提供以下三个属性。

    • event.source:发送消息的窗口
    • event.origin: 消息发向的网址
    • event.data: 消息内容

     下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。

    window.addEventListener('message', receiveMessage);
        function receiveMessage(event) {
        event.source.postMessage('Nice to see you!', '*');
    }

    event.origin属性可以过滤不是发给本窗口的消息。

    window.addEventListener('message', receiveMessage);
    function receiveMessage(event) {
        if (event.origin !== 'http://aaa.com') return;
        if (event.data === 'Hello World') {
            event.source.postMessage('Hello', event.origin);
        } else {
            console.log(event.data);
        }
    }

    【a.qq.com页面代码】

     【b.qq.com页面代码】

    以上demo简单解决了前端跨域通信,跨域带cookie等问题,在逻辑上完全可以实现跨域通信。但是对于不支持PostMessage特性的老版浏览器是行不通的。比如IE8-浏览器就不能很好的支持PostMessage特性

    6. 服务端代理

    在数据提供方没有提供对JSONP协议或者window.name协议的支持,也没有对其它域开放访问权限时,我们可以通过server proxy的方式来抓取数据。

    例如当www.a.com域下的页面需要请求www.b.com下的资源文件asset.txt时,直接发送一个指向 www.b.com/asset.txt的ajax请求肯定是会被浏览器阻止。

    这时,我们在www.a.com下配一个代理,然后把ajax请求绑定到这个代理路径下,例如www.a.com/proxy/, 然后这个代理发送HTTP请求访问www.b.com下的asset.txt,跨域的HTTP请求是在服务器端进行的,客户端并没有产生跨域的ajax请求。

    这个跨域方式不需要和目标资源签订协议,带有侵略性,另外需要注意的是实践中应该对这个代理实施一定程度的保护,比如限制他人使用或者使用频率。

    其他跨域方案

    window.name:

    在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的。

    动态创建script

    JSONP也就是利用这个原理。

    利用iframe和location.hash

    淘汰类技术

    利用flash

    淘汰类技术

    参考链接:

    http://tech.jandou.com/cross-domain.html

    http://www.cnblogs.com/JChen666/p/3399951.html

    http://www.ruanyifeng.com/blog/2016/04/cors.html

    用HTML5里的window.postMessage在两个网页间传递数据

  • 相关阅读:
    mac上python3安装HTMLTestRunner
    双目深度估计传统算法流程及OpenCV的编译注意事项
    深度学习梯度反向传播出现Nan值的原因归类
    1394. Find Lucky Integer in an Array
    1399. Count Largest Group
    1200. Minimum Absolute Difference
    999. Available Captures for Rook
    509. Fibonacci Number
    1160. Find Words That Can Be Formed by Characters
    1122. Relative Sort Array
  • 原文地址:https://www.cnblogs.com/chaoran/p/6579588.html
Copyright © 2011-2022 走看看