1. 程式人生 > >同源策略和跨域問題

同源策略和跨域問題

index dom 接口 dex 阻止 nodes strong 策略 服務器

1.同源策略

  指瀏覽器對不同源的腳本或者文本的訪問方式進行的限制。比如源a的js不能引入源b的元素屬性。

  所謂的同源是指兩個頁面具有相同的協議、主機(也是常說的域名)、端口,三個元素缺一不可。

  通過下列示例具體了解一下同源:

    技術分享圖片

  同源策略限制了不同源之間的交互,(同源策略限制了不同源之間的交互主要針對js中的XMLHttpRequest等請求)下面這些情況完全不受同源策略的限制的。

    1.頁面中的鏈接,重定向以及表單提交是不會受同源策略的影響。(在你的域名www.foo.com下面提交一個表單到www.bar.com是完全可以的)

    2.跨域資源嵌入是允許的,如果瀏覽器限制了JavaScript,就不能讀寫加載的內容。(如前面提到的嵌入的<script src="..."></script>,<img>,<link>,<iframe>等

),當然,如果要阻止iframe

     嵌入我們的網站資源時(頁面或者js等),我們可以在web既然有這麽多的情況是沒有同源策略限制的,那麽跨域問題是怎麽來的?

2.跨域問題

  前置條件是我們在web服務器或者服務端腳本中設置ACCESS-CONTROL-ALLOW-ORIGIN頭部,如果設置了這些頭部並且允許某些域名跨域訪問,則瀏覽器就會跳過同源策略的限制返回對應的內容。此外,如果你不是通過瀏覽器

  獲取資源,比如你通過一個Python腳本調用接口或者獲取js文件,也不在這個限制範圍之內。

  1.AJAX跨域

    通過AJAX調用其它域的接口會有跨域問題。比如:我在http://www.foo.com/index.html

中通過ajax調用請求http://www.bar.com/js/test.js頁面,此時是會報錯的。

      XMLHttpRequest cannot load http://www.bar.com/js/test.js.

      No ‘Access-Control-Allow-Origin‘ header is present on the requested resource.

      Origin ‘http://www.foo.com‘ is therefore not allowed access.

    這是因為我們的WEB服務器沒有設置ACCESS-CONTROL-ALLOW-ORIGIN

頭部,默認情況下是禁止跨域引用資源的。當然這裏有一點要註意,其實這個跨域請求是發送成功了的,服務器也有返回test.js內容,只是瀏覽器禁止

    Javascript去取response的數據而已。如果要設置ACCESS-CONTROL-ALLOW-ORIGIN頭部,nginx可以使用下面的代碼

      add_header ‘Access-Control-Allow-Origin‘ ‘http://www.foo.com‘;

       add_header ‘Access-Control-Allow-Credentials‘ ‘true‘;

      add_header ‘Access-Control-Allow-Methods‘ ‘GET,POST‘;

    另外,我們看到直接通過Javascript去取iframe中的元素也是會報錯的,因為域名不同。報錯如下所示

      Uncaught SecurityError: Failed to read the ‘contentDocument‘ property from ‘HTMLIFrameElement‘: Blocked a frame with origin "http://www.foo.com" from accessing a frame with origin "http://foo.com".

      The frame being accessed set "document.domain" to "foo.com", but the frame requesting access did not. Both must set "document.domain" to the same value to allow access.

    這時因為我們的主域名相同,因此可以在index.html和test.html中設置document.domain=‘foo.com‘來訪問iframe中的元素。註意,是兩個域名下面的文件都要設置,即便是同樣的主域名。當然這是特例,如果是兩個完全不同

    的域名,是沒有辦法的,你不能把www.foo.comdomain設置成www.163.com

    此外,在index.html裏面也可以看到通過<script>,<iframe>等標簽都是可以跨域內嵌資源的。

      # index.html

      <!DOCTYPE html>
      <html>
      <head>
      <title>test cross domain</title>
      <script src="/js/jquery.js"></script>
      <script src="http://www.bar.com/js/test.js"></script>
      <script>
      $(function(){
          document.domain = ‘foo.com‘; //1 註釋掉則會報錯 
          var ifr = document.getElementById("testframe");
          ifr.onload = function(){
              var doc = ifr.contentDocument || ifr.contentWindow.document;
              alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
          }
      });

      $.ajax("http://www.bar.com/js/test.js"); //2 報錯
      </script>

      </head>
      <body>
      <h1>Test Cross Domain</h1>
      <iframe id="testframe" src="http://foo.com/test.html"></iframe>
      </body>
      </html>
  
當然還可以通過iframe,location.hash,window.name,HTML5的postMessage等方法來實現跨域資源訪問
 2.JSONP跨域訪問(JSON+padding)
創建一個回調函數,然後在遠程服務上調用這個函數並且將JSON 數據形式作為參數傳遞,完成回調 將JSON數據填充進回調函數
   JSONP也是在開發中常見的內容,在jQuery中有封裝,通過ajax請求多帶上一個jsonp參數即可。JSONP也能夠實現跨域訪問資源。但是它的實現原理跟ajax沒有多大關系,它是通過動態插入<script>
   標簽來實現資源訪問的,在實際jsonp接口中,會讓你傳一個函數名過去,然後返回的數據中回調函數名就是你傳的函數名,回調函數的參數就是你封裝的jsonp格式,jQuery中的jsonp實現原理
   基本上就是這樣實現的。
      
# jsonp.html
      <script type="text/javascript">
          function callback(data) {
              alert(data.message);
          }
          function addScriptTag(src){
          var script = document.createElement(‘script‘);
              script.src = src;
              document.body.appendChild(script);
          }

          window.onload = function(){
             addScriptTag("http://www.foo.com/js/outer.js");
          }
      </script>

      # outer.js
      callback({message:"success"});










同源策略和跨域問題