跨域

    1. 跨域問題如何解決
      1. 什麼是跨域

同源策略:

所謂同源策略,它是瀏覽器的一種最核心最基本的安全策略。它對來至不同源的文檔或這腳本對當前文檔的讀寫操作做了限制。

同協議、ip、端口的腳本纔會執行。

只要協議、域名、端口有任何一個不同,都被當作是不同的域.

js跨域是指通過js在不同的域之間進行數據傳輸或通信.

      1. 爲什麼限制跨域

爲什麼要有這個策略,想必你已經知道,那就是因爲保證用戶的信息安全。

假如沒有同源策略

  1. 假設現在有a.com和b.com兩個域,如果沒有這一安全策略,那麼當用戶在訪問a.com時,a.com的一段腳本就可以在不加載b.com的頁面而隨意修改或者獲取b.com上面的內容。這樣將會導致b.com頁面的頁面發生混亂,甚至信息被獲取,包括服務器端發來的session。這樣的話,我們的web世界將是一片混亂。也是因爲瀏覽器的同源策略,保證來至不同源的對象不會互相干擾,保證了我們訪問頁面最基本的安全。
  2. 比如,cookie一般用於狀態控制,常用於存儲登錄的信息,如果允許跨域訪問,那麼別的網站只需要一段腳本就可以獲取你的cookie,從而冒充你的身份去登錄網站,造成非常大的安全問題,因此,現代瀏覽器均推行同源策略。
      1. 什麼會限制跨域

限制跨域

  1. cookie
  2. localStorage
  3. ajax請求

src可以跨域

在這裏需要注意的是,文檔中的所有帶“src”屬性的標籤都可以跨域加載資源,而不受同源策略的限制。

如<script>、<img>、<iframe>、<link>等。如果你在頁面定義了這些標籤,在頁面加載時都對不同源的資源發起了一次GET請求。但是通過src加載的資源,瀏覽器限制了腳本對其返回的內容無法讀寫。特別是在ajax請求的時候,特別要注意XMLHttpRequest的時候是無法跨域訪問的。

      1. 怎麼解決跨域
        1. 通過修改document.domain來跨子域

同源策略認爲域和子域屬於不同的域,如:

child1.a.com 與 a.com,

child1.a.com 與 child2.a.com,

xxx.child1.a.com 與 child1.a.com

兩兩不同源,可以通過設置 document.damain='a.com',瀏覽器就會認爲它們都是同一個源。想要實現以上任意兩個頁面之間的通信,兩個頁面必須都設置documen.damain='a.com'。

此方式的特點:

1. 只能在父域名與子域名之間使用,且將 xxx.child1.a.com域名設置爲a.com後,不能再設置成child1.a.com。

2. 存在安全性問題,當一個站點被攻擊後,另一個站點會引起安全漏洞。

3. 這種方法只適用於 Cookie 和 iframe 窗口。

 

        1. 使用window.name來進行跨域

window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的所有的頁面都是共享一個window.name的,每個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的所有頁面中的,並不會因新頁面的載入而進行重置。

// 打開必應 https://www.bing.com/

// 保留控制檯log(勾上Preserve log

 

> window.name

""

> window.name='text';

"text"

> location.href='http://www.google.com';

"http://www.google.com"

Navigated to https://www.google.com/

 

> window.name

"text

我們知道,使用iframe的src屬性,可以加載不同域中的網頁,

我們也可以使用$('iframe').contentWindow來拿到iframe中頁面的window對象,

只是這個window對象中可以訪問的屬性是很少的。

window.name這個屬性不是一個簡單的全局屬性 ---

只要在一個window下,無論url怎麼變化,只要設置好了window.name,那麼後續就一直都不會改變,同理,在iframe中,即使url在變化,iframe中的window.name也是一個固定的值,利用這個,我們就可以實現跨域了。

下面是localhost:8088/test2.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <title>test2</title>

</head>

<body>

  <h2>test2頁面</h2>

  <script>

    var person = {

      name: 'wayne zhu',

      age: 22,

      school: 'xjtu'

    }

    window.name = JSON.stringify(person)

  </script>

</body>

</html>

  即我們希望吧test2.html中的數據傳遞出去,到localhost:8081/test1.html中去。  

  下面是localhost:8081/test1.html

       這裏的意圖很明確,就是使用iframe將test2.html加載過來,因爲只是爲了實現跨域,所以將之隱藏,但是,這時已經完成了最重要的一步,就是將iframe中window.name已經成功設置,但是現在還獲取不了,因爲是跨域的,所以,我們可以把src設置爲當前域的proxy.html。

  另外,這裏之所以要設置flag,是因爲每當改變location的時候,就會重新來一次onload,所以我們希望獲取到數據之後,就直接close(),故採用此種方法。

這個proxy.html內容如下:

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <title>proxy</title>

</head>

<body>

  <p>這是proxy頁面</p>

</body>

</html>

  是的,什麼都沒有,當然,我們也可以把src就替換成test1.html,但是這樣的壞處很明顯,就是需要把test1.html中的數據加載一遍,這不是我們所希望的。

 

        1. 使用HTML5新提供的postMessage api

/* 子窗口 */

window.onmessage = function(e) {  

  if (e.origin !== 'http://bbb.com') return;  

  var payload = JSON.parse(e.data);  

  switch (payload.method) {  

    case 'set':  

      localStorage.setItem(payload.key, JSON.stringify(payload.data));  

      break;  

    case 'get':  

      var parent = window.parent;  

      var data = localStorage.getItem(payload.key);  

      parent.postMessage(data, 'http://aaa.com');  

      break;  

    case 'remove':  

      localStorage.removeItem(payload.key);  

      break;  

  }  

};  

/* 父窗口 */

var win = document.getElementsByTagName('iframe')[0].contentWindow;  

var obj = { name: 'Jack' };  

// 存入對象  

win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com');  

// 讀取對象  

win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");  

window.onmessage = function(e) {  

  if (e.origin != 'http://aaa.com') return;  

  // "Jack"  

  console.log(JSON.parse(e.data).name);  

};  

 

        1. 1. 通過jsonp跨域

jsonp在頁面上引入不同域上的js腳本文件實現請求不同域上的數據

(1) 通過script標籤引入一個js文件

(2) js文件載入成功後會執行我們在url參數中指定的函數,並且會把我們需要的json數據作爲參數傳入

注:需要服務器端的頁面進行相應的配合。

JSONP的原理:(舉例:a.com/jsonp.html想得到b.com/main.js中的數據)在a.com的jsonp.html裏創建一個回調函數xxx,動態添加<script>元素,向服務器發送請求,請求地址後面加上查詢字符串,通過callback參數指定回調函數的名字。請求地址爲http://b.com/main.js?callback=xxx。在main.js中調用這個回調函數xxx,並且以JSON數據形式作爲參數傳遞,完成回調。

function addScriptTag(src) {  

  var script = document.createElement('script');  

  script.setAttribute("type","text/javascript");  

  script.src = src;  

  document.body.appendChild(script);  

}

window.onload = function () {  

  addScriptTag('http://example.com/ip?callback=foo');  

}

//window.onload是爲了讓頁面加載完成後再執行

function foo(data) {  

   console.log(data.name+"歡迎您");

};

//b.com/main.js中的代碼

foo({name:"hl"})

這樣便實現了跨域的參數傳遞。

 

採用jsonp跨域也存在問題:

1. 使用這種方法,只要是個網站都可以拿到b.com裏的數據,存在安全性問題。需要網站雙方商議基礎token的身份驗證,這裏不詳述。

2. 只能是GET,不能POST。

3. 可能被注入惡意代碼,篡改頁面內容,可以採用字符串過濾來規避此問題。

客戶端頁面完整代碼

<!DOCTYPE html> 

<html> 

<head> 

<meta charset="utf-8"> <title>JSONP 實例</title> 

</head> 

<body> 

<div id="divCustomers"></div> 

<script type="text/javascript">

function callbackFunction(result, methodName) { 

var html = '<ul>';

for(var i = 0; i < result.length; i++) {

 html += '<li>' + result[i] + '</li>';

} 

html += '</ul>';

document.getElementById('divCustomers').innerHTML = html; }

</script> 

<script type="text/javascript" src="http://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction">

</script> 

</body>

 </html>
        1. jQuery 使用 JSONP:$.getJSON()

 

        1. WebSocket

WebSocket是HTML5開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。

 

在WebSocket API中,瀏覽器和服務器只需要做一個握手的動作,然後,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。

瀏覽器通過 JavaScript 向服務器發出建立 WebSocket 連接的請求,連接建立以後,客戶端和服務器端就可以通過 TCP 連接直接交換數據。

        1. CORS

CORS全稱cross-origin resource sharing,意爲跨站點資源共享,是w3c官方推薦的一種跨域方案,目前所有瀏覽器對其兼容性都表現良好,而且支持所有的請求方式,可以預見是未來最爲廣泛使用的跨域方案。

在支持CORS方案的瀏覽器中發送AJAX請求,請求地址爲目標域的絕對路徑時,請求頭中會帶有一個字段:withCredentials: true,這個字段會讓瀏覽器發送身份信息到服務端,如SSL、cookie等。與此同時,在服務端中設置響應頭中的Access-Control-Allow-Origin: *,則可以實現一個跨域請求。

注意:在實際使用中,最好對Access-Control-Allow-Origin進行指定域名的設置,如:Access-Control-Allow-Origin: www.test.com。如果使用Access-Control-Allow-Origin: *,則會允許所有來源進行跨域訪問,這會帶來比較大的安全問題。

        1.  服務器代理

Nginx proxy_pass

# 80 cat

upstream pig-client {

server 127.0.0.1:80 fail_timeout=0;

}

# app

upstream pig-control {

server 127.0.0.1:8080 fail_timeout=0;

}

#page 新陽藍光項目

upstream pig-xinyang {

server 127.0.0.1:4200 fail_timeout=0;

}



server {

listen 80;

server_name hardboy.cn;

index index.html index.htm;

location / {

proxy_pass http://pig-client;

try_files $uri $uri/ /index.html =404;

}

}

server {

listen 80;

server_name notapig.cn;

index index.html index.htm;

location / {

proxy_pass http://pig-xinyang;

try_files $uri $uri/ /index.html =404;

}

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章