什么是同源策略
- 同源策略限制從一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制。
- 一個(gè)源的定義:如果協(xié)議,端口(如果指定了一個(gè))和域名對(duì)于兩個(gè)頁(yè)面是相同的,則兩個(gè)頁(yè)面具有相同的源。
什么是跨域?跨域有幾種實(shí)現(xiàn)形式
- 不同源的資源之間的交互就是跨域。
- 實(shí)現(xiàn)跨域的幾種方式:
1.一些HTML標(biāo)簽和CSS語(yǔ)法能夠?qū)崿F(xiàn)跨源訪問(wèn):
- <script src="..."></script>標(biāo)簽嵌入跨域腳本。
- <link rel="stylesheet" href="...">標(biāo)簽嵌入CSS。由于CSS的松散的語(yǔ)法規(guī)則,CSS的跨域需要一個(gè)設(shè)置正確的Content-Type消息頭。
- <img>嵌入圖片。
- <video> 和 <audio>嵌入多媒體資源。
- <object>, <embed> 和 <applet>的插件。
- @font-face引入的字體。一些瀏覽器允許跨域字體,一些需要同源字體。
- <frame> 和 <iframe>載入的任何資源。站點(diǎn)可以使用X-Frame-Options消息頭來(lái)阻止這種形式的跨域交互。
2.使用JSONP:
-
JSONP的原理就是通過(guò)script標(biāo)簽?zāi)軌蚩缬蚯度肽_本來(lái)實(shí)現(xiàn)跨域通信的。示例:
btn.onclick = function () { var js = document.createElement('script') js.src = 'http://localhost:8080/jsonp?callback=renderPage' document.body.appendChild(js) document.body.removeChild(js) } function renderPage(data) { var node = '' for (var key in data) { node += '<li>' + data[key] + '</li>' } ct.innerHTML = node }
3.使用cors:
跨域資源共享( CORS )機(jī)制允許 Web 應(yīng)用服務(wù)器進(jìn)行跨域訪問(wèn)控制,從而使跨域數(shù)據(jù)傳輸?shù)靡园踩M(jìn)行。瀏覽器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 請(qǐng)求所帶來(lái)的風(fēng)險(xiǎn)。
-
CORS與普通的Ajax通信的區(qū)別就在于使用CORS需要在目標(biāo)服務(wù)器的HTTP頭中設(shè)置一個(gè)字段,從而聲明哪些源站有權(quán)限訪問(wèn)哪些資源。如:
服務(wù)器端: cors: function (res) { var data = router.news() res.setHeader("Access-Control-Allow-Origin", "*") res.end(JSON.stringify(data)) } 服務(wù)端返回的 Access-Control-Allow-Origin: * 表明,該資源可以被任意外域訪問(wèn)。 如果服務(wù)端僅允許來(lái)自 http://foo.example 的訪問(wèn),該首部字段的內(nèi) 容如下:Access-Control-Allow-Origin: http://foo.example
4.降域:
當(dāng)需要進(jìn)行跨域的資源位于統(tǒng)一頂級(jí)域下時(shí),可以更改它們的源來(lái)實(shí)現(xiàn)跨域訪問(wèn)。
-
源的更改:頁(yè)面可能會(huì)更改其自己的來(lái)源,但有一些限制。腳本可以將 document.domain 的值設(shè)置為其當(dāng)前域或其當(dāng)前域的超級(jí)域。如果將其設(shè)置為其當(dāng)前域的超級(jí)域,則較短的域?qū)⒂糜诤罄m(xù)原始檢查。例如:
假設(shè)文檔中的一個(gè)腳本在 http://store.company.com/dir/other.html 執(zhí)行以下語(yǔ)句:
document.domain = "company.com";
這條語(yǔ)句執(zhí)行之后,頁(yè)面將會(huì)成功地通過(guò)對(duì) http://company.com/dir/page.html 的同源檢測(cè)。而同理,company.com 不能設(shè)置 document.domain 為 othercompany.com.
瀏覽器單獨(dú)保存端口號(hào)。任何的賦值操作,包括document.domain = document.domain都會(huì)以null值覆蓋掉原來(lái)的端口號(hào)。賦值時(shí)必須帶上端口號(hào),以確保端口號(hào)不會(huì)為null。
5.postMessage:
window.postMessage() 方法可以安全地實(shí)現(xiàn)跨源通信。當(dāng)且僅當(dāng)執(zhí)行它們的頁(yè)面位于具有相同的協(xié)議,端口號(hào)(443為https的默認(rèn)值),以及主機(jī)(模數(shù) Document.domain 由兩個(gè)頁(yè)面設(shè)置為相同的值)。
-
window.postMessage() 方法被調(diào)用時(shí),會(huì)在所有頁(yè)面腳本執(zhí)行完畢之后向目標(biāo)窗口派發(fā)一個(gè) MessageEvent 消息。 示例:
a頁(yè)面: var ipt = document.querySelector('#ipt') ipt.addEventListener('input', function () { window.frames[0].postMessage(this.value, '*') }) // 監(jiān)聽(tīng)其它頁(yè)面發(fā)送來(lái)的消息 window.addEventListener('message', function (e) { ipt.value = e.data }) b頁(yè)面: var ipt = document.querySelector('#ipt') ipt.addEventListener('input', function () { window.parent.postMessage(this.value, '*') }) window.addEventListener('message', function (e) { ipt.value = e.data })