同源策略、几种跨域方式

概念

1、什么是同源策略?

  • URL(Uniform Resource Locator)
    统一资源定位符,俗称网址


    URL组成
    URL组成
  • 什么是源(origin)

  • 指页面的协议、域名、端口号

  • 通过location.origin可以查看当前页面的源(IE不支持,可以分开写location.protocol;location.hostname;location.port

    页面的源
    页面的源

  • 什么是同源策略(Same Origin Policy)

  • 是浏览器的一个功能;

  • 同源就是指协议、域名、端口号相同;

  • 不同源的客户端脚本在没明确授权的情况下,不能读写对方的资源;

  • 什么不是同源策略


    引用不是同源策略
    引用不是同源策略

    上图这种跨站引用资源不受同源策略的限制,这算是浏览器的一个妥协(让你下载别的网站的资源来使用),引用过来并没有去读和写它们的内容。我引用过来的资源就在当前的域名了,可以对当前域名下的资源读写,而不能读写原来域名的资源。如果我不是引用而是发起Ajax请求的话便会受到同源策略的限制。

  • 有什么作用
    浏览器出于安全方面的考虑,不允许跨域调用其他页面的对象,防止恶意网站盗窃数据。但在安全限制的同时也给注入iframe或是Ajax应用上带来了不少的麻烦,所以有时候就必须跨域。

2、什么是跨域,跨域有几种实现形式?

  • 出于同源策略的限制,不同源的客户端脚本是不能读写对方资源的,而跨域就是突破同源策略的限制,实现跨源通信(协议和端口号不同造成的问题前台是无能为力的)。

  • 跨域的实现形式:
    (1)、降域[document.domain+iframe]:思路是让不同源的变成同源。

    • 通过降域可以使两个一级域名相同, 二级域名不同的网页实现跨域资源共享, 但是ajax请求不能通过这种方式跨域, 只有cookie和iframe形式的跨域可以用降域来实现;
      被同源策略限制
      被同源策略限制
    • 具体做法是在两个页面文件脚本中都加入document.domain = "jay.com";,这样两个页面的js文件就可以互相通信了。
      跨域成功
      跨域成功
    • 缺点:
      1、这种方案容易出现大面积的安全问题,只要任何一个子域名被攻击,那么主域名下的信息也会被泄露;
      2、只对iframe形式的跨域有效;
      3、只对带有同样后缀的域名有效;

(2)、JSONP[JSON with Padding]

  • JSONP和JSON没有任何关系,仅仅是因为最后生成的JS文件中的数据一般是JSON格式的,像这样printData({name: '周花花', age: 20});因此得名;
  • 我们知道a.com可以引用b.com的JS文件,那是不是可以在b.com的JS文件里面放数据?
  • 实现就是a.com和b.com约定好相同的函数名,a.com中定义这个函数,在b.com中包含这个函数名并且在后面加个括号中间放入数据,即数据名({json数据});,但是b.com中的这个东西在a.com看来就是一个函数,它会调用a.com定义的函数。所以在a.com中的函数就被调用并执行,b.com中的数据被当作形参传入a.com定义的函数中。
  • JSONP就是服务端动态的生成JS。由于JSONP的服务者要面对很多服务对象,而这些服务对象各自的本地函数都不一样,所以为了让远程的JS知道它应该调用的本地函数叫什么名字,客户端传一个参数告诉服务端我想要一段调用XXX函数的JS代码,请你返回给我,于是服务器就可以按照客户端的需求来生成JS脚本并响应了。
    客户端代码:
<!--不建议直接这样引用,我们可以将动态生成script标签封装成一个函数,每次使用的时候直接调用函数-->
<script src="//www.zhouhuahua.com/data.js?callback=onPrintData"></script>
<script>   
  //www.zhoupenghui.com请求www.zhouhuahua.com下面的数据    
  //首先定义一个函数,服务端生成的JS会调用并执行这个函数
  function onPrintData(data){        
        console.log(data);    
  }
  //封装一个生成script标签的函数   
  function printData(){       
        var script = document.createElement('script');       
        script.src = "//www.zhouhuahua.com/data.js?callback=onPrintData";
        document.body.appendChild(script);
  }    
  printData();
</script>
<!--用jquery实现jsonp调用-->
<script>
  $(function(){
    $.ajax({
      type: "get",
      url: "//www.zhouhuahua.com/data.js?callback=printData",
      dataType: "jsonp",
      jsonp: "callback",
      jsonpCallback: "printData",
      success: function(data){
        console.log(data);
      },
      error: function(){
        alert('fail');
      }
    });
  });
</script>

服务器端生成的JS文件:

onPrintData({    
    name: '周花花',    
    age: 22
});
跨域请求得到数据
跨域请求得到数据
  • 缺点:
    1、安全性问题,所有的网页都能拿到data.js里面的内容,所以需要校验身份。
    2、因为是基于script所以无法触发post请求,只能获取不能写。
    3、可能被注入( callback=alert(something); )。

(3)、CORS[Cross-Origin Resource Sharing跨域资源共享]

  • b.com声明:我允许a.com来访问我,在响应头添加一个"Access-Control-Allow-Origin:http://www.zhoupenghui.com"即可;
  • a.com中的js发起对b.com的ajax
    客户端正常发起ajax请求:
<script>
    document.querySelector("#btn").addEventListener('click', function(){
        $.ajax({
            url: '//www.zhouhuahua.com/CORS.php',   //接口地址即请求地址
            type: 'get',   // 请求类型, post 或者 get, 
            data: {   //发送给后台的数据
                username: document.querySelector('#username').value
            }, 
            success: function(jsonData){   //响应成功(得到的数据可以是对的也可以是不对的,总之会有一个响应)执行这个函数
                var jsonObject = JSON.parse(jsonData);  //将后台传的字符串转换为JSON对象
                dealwith(jsonObject);  //这里的处理函数和上面的一样
            }, 
            error: function(){   //失败(没任何响应,比如服务器宕机了啥的)执行这个函数
                console.log('出错了') 
             } 
        });
    });
    function dealwith(userInfo){  //对解析出来的JSON对象操作
        //我们可以用从服务器拿到的数据,返回的数据是由后台给的字符串,所以我们还要写php
        var str = '<dt>性别:</dt>';
        str +=  '<dd>'+userInfo.sex+'</dd>';
        str += '<dt>年龄:</dt>';
        str +=  '<dd>'+userInfo.age+'</dd>';
        document.querySelector('#ct').innerHTML = str;
    }
</script>

服务端只要加一个响应头内容:

<?php   
    header("Access-Control-Allow-Origin:http://www.zhoupenghui.com");
    header('Content-Type:text/html;charset=utf-8');   
    $usename = $_GET['username'];   
    if($usename === 'kevin'){       
        $ret = array('sex'=>'男', 'age'=>23);   
    }else if($usename === 'david'){       
        $ret = array('sex'=>'男', 'age'=>30);   
    }else{       
        $ret = array('sex'=>'女', 'age'=>18);  
    }  
    echo json_encode($ret);
CORS跨域获取数据
CORS跨域获取数据
  • 优缺点:比JSONP强大,支持所有的http请求,JSONP的优势在于支持老式的浏览器以及可以向不支持CORS的网站请求数据。

(4)、HTML5 postMessage

  • 这是HTML5的一个功能
    语法为:otherWindow.postMessage(message, targetOrigin);
  • 在a.com中用iframe.contentWindow.postMessage('message',targetOrigin )给b.com发送消息,在b.com中给window绑定message事件,如果message触发我先判断是不是a.com发来的数据。
    a.com中代码:
<iframe id="ifr" src="//www.zhouhuahua.com/postMess.html" frameborder="1"></iframe>
<script>    
    window.onload = function(){        
        var ifr = document.getElementById("ifr");        
        var targetOrigin = 'http://www.zhouhuahua.com';
        ifr.contentWindow.postMessage('我是来自zhoupenghui.com的数据', targetOrigin);    
    }
</script>

b.com中代码:

<script>    
    window.addEventListener('message', function(event){        
        if(event.origin === 'http://www.zhoupenghui.com'){
            console.log('from:'+event.origin);            
            console.log('zhoupenghui传来的数据:'+event.data);        
        }    
    }, false);
</script>
postMessage跨源通讯
postMessage跨源通讯

(5)、其他hack

  • hash
  • window.name

3、jsonp的原理是什么?

  • 不同源的页面数据不能互相读写,但是可以通过script标签引用;
  • 在被引用的对象中存放数据;
  • 动态引用的同时,提供一个回调函数来接收数据;
  • JSONP的本质就是动态的添加script标签来调用服务器提供的JS脚本。

2、CROS是什么?

  • CORS是一个W3C标准,它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服ajax只能同源使用的限制。
  • CORS需要浏览器和服务器同时支持,目前所有的浏览器都支持该功能,IE浏览器不能低于IE10。


    CORS的兼容性
    CORS的兼容性
  • 当我们读取另一个站点的资源时,支持CORS的浏览器实际上就已经帮我们发起跨站请求了,只是我们没有得到对方网站的允许,所以浏览器将返回的结果拦截,告诉你不能跨域请求资源。只有当对方允许你读取之后,浏览器才会返回你想要的信息。

练习

1、在本地搭建服务器,演示同源策略

  • 本地搭建服务器,多站点配置:


    xampp本地多站点配置
    xampp本地多站点配置
  • 修改本地hosts文件,让多个不同域名映射到本地服务器:


    本地hosts文件修改
    本地hosts文件修改

    不同域名访问本地服务器
    不同域名访问本地服务器
  • www.zhoupenghui.comwww.zhouhuahua.com发送ajax请求失败,就是受浏览器同源策略的限制。
    同源策略限制不同域名之间通信
    同源策略限制不同域名之间通信

参考:
JavaScript跨域总结与解决办法
说说json和jsonp
跨域资源共享 CORS详解

推荐阅读更多精彩内容