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在两个网页间传递数据

  • 相关阅读:
    输入年月日,输出这一天是这一年的多少天
    判断体重是否标准 男标准=身高-100±3 女标准=身高-110±3
    if 条件运算符
    24小时换算成12小时&&判断正负数
    运算符(编程)
    定义变量
    基础知识
    java线程阻塞中断与LockSupport使用介绍(转)
    01背包问题--动态规划解法(2)(转载)
    01背包问题--动态规划解法
  • 原文地址:https://www.cnblogs.com/chaoran/p/6579588.html
Copyright © 2011-2022 走看看