天天看點

浏覽器的同源政策及jsonp跨域請求

1.什麼是同源政策

浏覽器的同源政策限制從一個源加載的文檔或腳本與來自另一個源的資源進行互動。

兩個頁面位址中的協定、域名和端口号一緻,則表示同源。

例如位址 https://www.google.com 和以下位址對比

位址 同源 原因
http://www.google.com 協定不一緻
https://google.com 域名不一緻
https://www.google.com:81 端口号不一緻
https://www.google.com/a/s.html 協定,域名和端口号都一緻

同源政策的限制:

  • 存儲在浏覽器中的資料,如localStroage、Cooke和IndexedDB不能通過腳本跨域通路;
  • 不能通過腳本操作不同域下的DOM;
  • 不能通過ajax請求不同域的資料。

2.為什麼要同源政策

跨域的安全限制是針對浏覽器端來說的,伺服器端不存在跨域安全限制。

設定同源限制主要是為了安全,如果沒有同源限制存在浏覽器中的cookie等其他資料可以任意讀取,不同域下DOM任意操作,Ajax任意請求的話如果浏覽了惡意網站那麼就會洩漏這些隐私資料。

Cookie一般用來儲存登入狀态。在登入一個銀行網站後此時浏覽器中就儲存了登入的狀态,同時浏覽了惡意網站,這時Cookie的資訊沒有同源限制的話惡意網站就可以擷取這些Cookie資訊來達到不為人知的目的。

如果可以操作不同域下的DOM可以用如下方式完成盜取資訊。在自己的網站上嵌入一個iframe位址設定成銀行位址,然後讓iframe全屏顯示,當你一不小心上當了輸入你賬号密碼,我就可以通過DOM操作擷取到輸入的資訊。

Ajax的限制同Cookie,如果帶上Cookie去跨域通路接口就可以通過程式的驗證被認為身份是合法的。

既然瞥見危害一角自然要嚴加防範,限制非同源操作。

3.Ajax同源政策的規避

一個網站中的資料有時并不在同一台伺服器上,這時就需要跨域調用資料,下面是ajax規避同源政策的方法。

雖然跨域限制了Ajax請求,但是卻并不影響跨域引用腳本。

<script src="http://example.com/index.php?arg=val1&jsonp=callback"/>
<script>
    callback(data){
        console.log(data); 
    }    
</script>
           
<?php
    echo $_GET['jsonp'] . '(' . '資料' . ')';
?>
           

上面的 index.php 接口傳回的是一段調用 函數的js代碼 callback(data),其中data就是接口要傳回的資料。而這個callback是事先在頁面上寫好的處理資料的回調函數,并且回調函數的名稱通過URL參數的形式傳遞給了後端接口,這樣就完成了一次跨域。

再看jquery的jsonp方式跨域請求:

服務端代碼不變,js代碼如下:最簡單的方式,隻需配置一個dataType:’jsonp’,就可以發起一個跨域請求。jsonp指定伺服器傳回的資料類型為jsonp格式,可以看發起的請求路徑,自動帶了一個callback=xxx,xxx是jquery随機生成的一個回調函數名稱。

看jquery源碼,可以看到jsonp調用回調函數時,是調用的window.callback。

可以通過設定參數jsonpCallback: “xxx”,指定回調函數名稱。

通過設定jsonp: “xxxx”,改變callback這個名稱,指定參數名稱。

指定callback這個名稱後,背景也需要跟着更改。

前端代碼:

<html>
  <head>
    <title>跨域測試</title>
    <script src="js/jquery-1.7.2.js"></script>
    <script>
      function showData (data) 
        console.info("調用showData");
        var result = JSON.stringify(data);
        $("#text").val(result);
      }
      $(document).ready(function () {
        $("#btn").click(function () {
          $.ajax({
            url: "http://localhost:9090/student",
            type: "GET",
            dataType: "jsonp",  //指定伺服器傳回的資料類型
            jsonp: "theFunction",   //指定參數名稱
            jsonpCallback: "showData",  //指定回調函數名稱 
            success: function (data) {
              console.info("調用success");
            }
          });
        });
      });
    </script>
  </head>
  <body>
    <input id="btn" type="button" value="跨域擷取資料" />
    <textarea id="text" style="width: 400px; height: 100px;">  
    </textarea> 
  </body>
</html>
           

後端代碼:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  response.setCharacterEncoding("UTF-8");
  response.setContentType("text/html;charset=UTF-8");
  //資料
  List<Student> studentList = getStudentList();
  JSONArray jsonArray = JSONArray.fromObject(studentList);
  String result = jsonArray.toString();
  //前端傳過來的回調函數名稱
  String callback = request.getParameter("theFunction");
  //用回調函數名稱包裹傳回資料,這樣,傳回資料就作為回調函數的參數傳回去了
  result = callback + "(" + result + ")";
  response.getWriter().write(result);
}
           

注:jsonp隻支援GET請求。

jQuery ajax方式以jsonp類型發起跨域請求,其原理跟script腳本請求一樣,是以使用jsonp時也隻能使用GET方式發起跨域請求。跨域請求需要服務端配合,設定callback,才能完成跨域請求。

上文:Vue請求資料——HTTP庫axios

更多内容,歡迎關注微信公衆号“讓知識成為資産”。

繼續閱讀