0%

跨域问题

之前写的安卓程序,并不存在跨域这种说法,是可以自由的从不同的域名下请求数据的,现在看前端知识,则就出现了跨域这个概念。了解跨域,主要清楚以下的知识点就可以。

  • 什么是同源策略,满足同源策略的三个条件是什么
  • 跨域的具体方案是什么,怎么处理。

什么是同源策略,满足同源策略的三个条件是什么

浏览器为了安全起见,制定了同源策略,只允许在本域名下的数据请求操作,同源策略主要要求以下三个条件

  • 协议相同,注意http和https是不同的协议
  • 域名相同
  • 端口一致

请求的url和当前html的url满足以上三个条件就是同源的,非同源的情况下,使用数据就会受到限制。

跨域的具体方案是什么,怎么处理

实际场景中,我们不可能什么东西都放在自己的域名下来处理,比如我们可能需要一个天气请求的API,但是我们的主业是电商,那我们很可能就需要一个第三方的API来做。这个时候我们就不得不跨域。目前主流的跨域的方案有一下三类。

  1. JSONP

    JSONP是JSON with padding的简称。我们知道 Script 标签中的js不受同源策略的影响,我们可以自由加载任何域名下的js来执行。JSONP就是以此为契机建立的。具体的我们在执行的时候可以这么做

    1. 在html端

      1
      2
      3
      4
      5
      <script src = "https://a.imzy.me?callback = getWeather">
      function getWeather(data){
      console.log(data)
      }
      </script>
    2. 在服务端返回的时候 带上 callback({data: xxx})。

      前后端配合,实现JSONP。不过以上的做法显得有些tricking。最主要的缺点是他只可以进行get请求。

  2. CROS

    CROS的意思是跨域资源共享,这种跨域的方法是对ajax的拓展。

    CROS机制是浏览器发现请求的链接和当前页面url是不同源的,就主动在请求头中添加一个Origin的头,然后后端设置一个Access-Control-Allow-Origin的响应头。响应头中带有服务端允许的域。允许的域和Origin中的域能对应上就可以使用CROS。主要需要后端设置响应头。

    详细解释下,Origin头说明的是当前请求来自于哪个域,ACAO则说明的是服务端允许的域。

    可以参考 CROS.

  3. 和iframe有关的行为

    在html页面上,我们也可以使用iframe来嵌套另一个html页面。我们的主页面,一般是不允许控制iframe中的元素的,除非iframe和我们的主页面是同一个域。不过iframe确实存在需要跨域的场景。和iframe有关的跨域主要分为以下两种

    1. 降域。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      a.html
      <html>
      <style>
      .ct{
      width: 910px;
      margin: auto;
      }
      .main{
      float: left;
      width: 450px;
      height: 300px;
      border: 1px solid #ccc;
      }
      .main input{
      margin: 20px;
      width: 200px;
      }
      .iframe{
      float: right;
      }
      iframe{
      width: 450px;
      height: 300px;
      border: 1px dashed #ccc;
      }
      </style>

      <div class="ct">
      <h1>使用降域实现跨域</h1>
      <div class="main">
      <input type="text" placeholder="http://a.nany.com:8080/a.html">
      </div>

      <iframe src="http://b.imzy.vip:8080/b.html" frameborder="0" ></iframe>

      </div>

      <script>
      //URL: http://a.imzy.vip:8080/a.html
      document.querySelector('.main input').addEventListener('input', function(){
      console.log(this.value);
      window.frames[0].document.querySelector('input').value = this.value;
      })
      document.domain = 'imzy.vip'
      </script>
      </html>

      以下是iframe中的代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <html>
      <style>
      html,body{
      margin: 0;
      }
      input{
      margin: 20px;
      width: 200px;
      }
      </style>

      <input id="input" type="text" placeholder="http://b.imzy.vip:8080/b.html">
      <script>
      // URL: http://b.imzy.vip:8080/b.html

      document.querySelector('#input').addEventListener('input', function(){
      window.parent.document.querySelector('input').value = this.value;
      })
      document.domain = 'imzy.vip';
      </script>
      </html>

      注意以上代码中,a.imzy.vip和b.imzy.vip是不同的域名。但是他们的后缀是一样的,所以可以降域为imzy.vip

    2. PostMessage

      主要使用的是iframe的postMessage方法,来进行iframe的通信。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title>跨域POST消息发送</title>
      <script type="text/JavaScript">
      // sendPost 通过postMessage实现跨域通信将表单信息发送到 指定的域名上,
      // 并取得返回的数据
      function sendPost() {
      // 获取id为otherPage的iframe窗口对象
      var iframeWinow = document.getElementById("otherPage").contentWindow;
      // 向该窗口发送消息
      iframeWin.postMessage(document.getElementById("message").value,
      'http://imzy.vip');
      }
      // 监听跨域请求的返回
      window.addEventListener("message", function(event) {
      console.log(event, event.data);
      }, false);
      </script>
      </head>
      <body>
      <textarea id="message"></textarea>
      <input type="button" value="发送" onclick="sendPost()">
      <iframe
      src="http://imzy.vip/other-domain.html" id="otherPage"
      style="display:none"></iframe>
      </body>

      </html>

      放在另一个域名下的iframe内容

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title>POST Handler</title>
      <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
      <script type="text/JavaScript">
      window.addEventListener("message", function( event ) {
      // 监听父窗口发送过来的数据向服务器发送post请求
      var data = event.data;
      $.ajax({
      // 注意这里的url只是一个示例.实际练习的时候你需要自己想办法提供
      type: 'POST',
      url: 'http://imzy.vip/getData',
      data: "info=" + data,
      dataType: "json"
      }).done(function(res){
      //将请求成功返回的数据通过postMessage发送给父窗口
      window.parent.postMessage(res, "*");
      }).fail(function(res){
      //将请求失败返回的数据通过postMessage发送给父窗口
      window.parent.postMessage(res, "*");
      });
      }, false);
      </script>
      </head>

      <body></body>
      </html>

      以上是通过PostMessage来进行iframe通信的实例,主要是完成了,从主界面发送消息到子iframe,子iframe中监听消息,将数据使用window.parent.postMessage()方法,回传数据的过程。