瀏覽器同源策略和跨域方法

https://www.jianshu.com/p/1d0ee9bac639

http://www.javashuo.com/article/p-mrypbjlr-z.html

http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

http://www.ruanyifeng.com/blog/2016/04/cors.html

瀏覽器同源策略及處理辦法

1、什麼是瀏覽器的同源策略

同源是指同協議同域名同端口javascript

 
同源策略.png

 

注:IE 未將端口號加入到同源策略的組成部分之中php

瀏覽器同源策略的目的是爲了保證用戶信息的安全,防止惡意的網站竊取數據。若是網頁之間不知足同源要求,將不能:html

  1. 共享Cookie、LocalStorage、IndexDB
  2. 獲取DOM
  3. AJAX請求不能發送

2、既然知道這一策略,開發中如何規避呢?

(1)經過設置window.domain的方式實現共享cookie

這種方法適用於一級域名相同,二級域名不一樣的時候使用前端

例如:如今有www.example.comexample.com兩個網頁,能夠經過設置window.domain的形式來處理,前端頁面能夠經過設置window.domain=example.com或者後臺設置Set-Cookie: key=value; domain=.example.com; path=/來實現共享cookiejava

(2)經過修改片斷標識符的方法實現跨域

這種方法適用於iframe嵌套網頁間的跨域。片斷標識符就是URL的#號後面的部分。改變片斷標識符,頁面不會刷新。web

父窗口向子窗口傳遞信息ajax

//父窗口 var src = originURL + '#' + data; document.getElementById('myIFrame').src = src; //子窗口經過監聽hashchange事件回去父窗口傳遞的信息 window.onhashchange = checkMessage; function checkMessage() { var message = window.location.hash; // ... } 

子窗口向父窗口傳遞信息json

//子窗口修改父窗口的片斷標識符,一樣,父窗口也能夠經過hashchange事件來獲取數據 parent.location.href= target + "#" + hash; 

(3)使用window.name處理跨域問題

window.name屬性具備以下特色,不管是否同源,只要在同一窗口中設置了該屬性,後一個網頁就能夠讀取到。該方法藉助window.name這一屬性和iframe具備跨域能力的關鍵點,來處理跨域問題。後端

首先,在localhost:8080服務器建立文件demo.html和空白文件proxy.html,而後,在localhost:8081服務器建立data.html文件,文件內容以下所示:跨域

//localhost:8080/demo.html
<!doctype html> <html> <head> <title>demo</title> </head> <body> <h1>demo</h1> <iframe id="myIframe" width="400" height="400"></iframe> <script> var myIframe = document.getElementById('myIframe'); myIframe.src = 'http://localhost:8081/yzdj-mobile-web/data.html'; var state = 0; myIframe.onload = function(){ if(state === 1) { console.log(myIframe.contentWindow.name); myIframe.contentWindow.document.write(''); myIframe.contentWindow.close(); }else if(state === 0){ state = 1; myIframe.contentWindow.location = 'http://localhost:8080/yzdj-admin-web/proxy.html'; } } </script> </body> </html> 
//localhost:8081/data.html
<!doctype html> <html> <head> <title>data</title> </head> <body> <h1>data</h1> <script type="text/javascript"> window.name = '{a:1,b:2}'; </script> </body> </html> 

這種解決方法的優勢是window.name容量大,缺點是:須要監聽window.name變化,影響網頁性能。

(4)使用window.postMessage解決跨域

這種方法藉助HTML5的跨文檔API(cross-document messaging)中增長的window.postMessage方法,容許跨窗口通訊,無論窗口是否同源。父窗口和子窗口能夠監聽message事件,獲取對方發送的消息。這種方法能夠實現LocalStorage等信息的共享。

messageevent對象提供三個屬性:

  • event.source:發送消息的窗口
  • event.origin: 消息發向的網址
  • event.data: 消息內容

下面分別使用window.open的方法和嵌套iframe的方法實現父子窗口之間的通訊
使用window.open方法父頁面:

//localhost:8080
<!doctype html> <html> <head> <title>admin</title> </head> <body> <h1 class="header">parent</h1> <div class="mb20"> <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea> <button style="font-size:20px;" onclick="send()">post message</button> </div> <script> var pop = window.open('http://localhost:8181/yzdj-mobile-web/demo.html'); function send() { var data = document.querySelector('#data').value; pop.postMessage(data, 'http://localhost:8181/'); // 觸發跨域子頁面的messag事件 } window.addEventListener('message', function(messageEvent) { var data = messageEvent.data; console.info('message from child:', data); }, false); </script> </body> </html> 

子頁面:

//localhost:8081
<!doctype html> <html> <head> <title>mobile</title> </head> <body> <h1 class="header">chidren</h1> <input type="text" id="inp" value="some contents.."> <button onclick="send()">send</button> <script> var origin =''; var source = ''; window.addEventListener('message', function(ev) { var data = ev.data; origin = ev.origin; source = ev.source console.info('message from parent:', data); }, false); function send() { var data = document.querySelector('#inp').value; source.postMessage(data, origin); // 若父頁面的域名和指定的不一致,則postMessage失敗 } </script> </body> </html> 

使用iframe嵌套的方法,父頁面

<!doctype html> <html> <head> <title>parent</title> </head> <body> <h1 class="header">parent</h1> <div class="mb20"> <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea> <button style="font-size:20px;" onclick="send()">post message</button> </div> <!-- 跨域的狀況 --> <iframe src="http://localhost:8081/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe> <script> function send() { var data = document.querySelector('#data').value; window.frames[0].postMessage(data, 'http://localhost:9022/'); // 觸發跨域子頁面的messag事件 } window.addEventListener('message', function(messageEvent) { var data = messageEvent.data; console.info('message from child:', data); }, false); </script> </body> </html> 

子頁面

<!doctype html> <html> <head> <title>chilren</title> </head> <body> <h1 class="header">chilren</h1> <input type="text" id="inp" value="some contents.."> <button onclick="send()">send</button> <script> window.addEventListener('message', function(ev) { var data = ev.data; console.info('message from parent:', data); }, false); function send() { var data = document.querySelector('#inp').value; parent.postMessage(data, 'http://localhost:9011/'); // 若父頁面的域名和指定的不一致,則postMessage失敗 } </script> </body> </html> 

(5)使用JSONP來實現AJAX的跨域

定義和用法:經過動態插入一個script標籤。瀏覽器對script的資源引用沒有同源限制,同時資源加載到頁面後會當即執行(沒有阻塞的狀況下)。

特色:經過狀況下,經過動態建立script來讀取他域的動態資源,獲取的數據通常爲json格式。

實例以下:

<script>

    function testjsonp(data) {

       console.log(data.name); // 獲取返回的結果

    }

</script>

<script>

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

    _script.type = "text/javascript";

    _script.src = "http://localhost:8888/jsonp?callback=testjsonp";

    document.head.appendChild(_script);

</script>

動態建立一個 script 標籤,而且告訴後端回調函數名叫 handleResponse
// 1. 定義一個 回調函數 handleResponse 用來接收返回的數據
function handleResponse(data) { console.log(data); };
// 2.var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.src = 'http://www.laixiangran.cn/json?callback=handleResponse';
body.appendChild(script);
// 3.經過 script.src 請求 `http://www.laixiangran.cn/json?callback=handleResponse`,
// 4. 後端可以識別這樣的 URL 格式並處理該請求,而後返回 handleResponse({"name": "laixiangran"}) 給瀏覽器
// 5. 瀏覽器在接收到 handleResponse({"name": "laixiangran"}) 以後當即執行 ,也就是執行 handleResponse 方法,得到後端返回的數據,這樣就完成一次跨域請求了。

或者使用jq的ajax方法

$.ajax({
    url:'http://www.example.com/getData', dataType:'jsonp', jsonp:'callback', data:{ wd:'XX.value' }, success:function(result){ alert(result.s); } , error:function(err){ alert(err) } }); 

這種方法須要和設置修改請求網址的後臺,代碼中的callback須要和後臺設置的一致

優勢

  • 使用簡便,沒有兼容性問題,目前最流行的一種跨域方法。

缺點

  • 只支持 GET 請求。
  • 因爲是從其它域中加載代碼執行,所以若是其餘域不安全,極可能會在響應中夾帶一些惡意代碼。
  • 要肯定 JSONP 請求是否失敗並不容易。雖然 HTML5 給 script 標籤新增了一個 onerror 事件處理程序,可是存在兼容性問題。

(6)webSocket

是一種通訊協議,使用ws://wss://做爲協議前綴,這種協議不受同源政策的影響,只要服務器支持就能夠。

(7)CORS(Cross-Origin Resource Sharing,跨源資源分享)

它容許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。這種解決跨域方法的優勢是容許任何形式的請求,而JSONP只能發GET請求。

實現CORS通訊的關鍵是服務器,只要服務器實現了CORS接口,就可跨源通訊

簡單請求(simple request)和非簡單請求(not-so-simple request)

(1) 請求方法是如下三種方法之一:
HEAD
GET
POST
(2)HTTP的頭信息不超出如下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

不一樣時知足上面條件的就是非簡單請求。

對於簡單請求,瀏覽器直接發出CORS請求。具體來講,就是在頭信息之中,增長一個Origin字段,用來講明本次請求的是哪一個源。服務器根據這個字段來判斷是否容許請求。

非簡單請求是那種對服務器有特殊要求的請求,好比請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
對於非簡單請求,會在正式通訊以前,增長一次HTTP查詢請求,稱爲預檢(preflight)請求。

服務器收到"預檢"請求之後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段之後,確認容許跨源請求,就能夠作出迴應。一旦服務器經過了"預檢"請求,之後每次瀏覽器正常的CORS請求,就都跟簡單請求同樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。

優勢

  • CORS 通訊與同源的 AJAX 通訊沒有差異,代碼徹底同樣,容易維護。
  • 支持全部類型的 HTTP 請求。

缺點

  • 存在兼容性問題,特別是 IE10 如下的瀏覽器。
  • 第一次發送非簡單請求時會多一次請求。

(8)還有一種方法是使用Nginx轉發。

相關文章
相關標籤/搜索