在了解跨域之前,我們先了解下什么是同源策略
1.什么是同源策略?
同源策略/SOP(Same origin policy)是一種約定憋沿,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能亚斋,如果缺少了同源策略毅待,瀏覽器很容易受到XSS檐涝、CSFR等攻擊拂到。所謂同源是指"協(xié)議+域名+端口"三者相同痪署,即便兩個(gè)不同的域名指向同一個(gè)ip地址,也非同源兄旬。
同源:
不同源:
- http://jirengu.com/main.js 和 https://jirengu.com/a.php (協(xié)議不同)
- http://jirengu.com/main.js 和 http://bbs.jirengu.com/a.php (域名不同狼犯,域名必須完全相同才可以)
- http://jiengu.com/main.js 和 http://jirengu.com:8080/a.php (端口不同,第一個(gè)是80)
需要注意的是: 對(duì)于當(dāng)前頁(yè)面來(lái)說(shuō)頁(yè)面存放的 JS 文件的域不重要,重要的是加載該 JS 頁(yè)面所在什么域
2.什么是跨域领铐?
瀏覽器從一個(gè)域名的網(wǎng)頁(yè)去請(qǐng)求另一個(gè)域名的資源時(shí)悯森,域名、端口罐孝、協(xié)議任一不同呐馆,都是跨域
3.跨域的幾種解決方式
-
使用JSONP方式實(shí)現(xiàn)跨域:
原理:JSONP是通過(guò)script
標(biāo)簽加載數(shù)據(jù)的方式去獲取數(shù)據(jù)當(dāng)做JS代碼來(lái)執(zhí)行。具體操作方法:提前在頁(yè)面上聲明一個(gè)函數(shù)(這個(gè)函數(shù)用來(lái)處理后端返回的數(shù)據(jù))莲兢,函數(shù)名通過(guò)接口的方式傳遞給后臺(tái)汹来;后臺(tái)解析到函數(shù)名后在原始數(shù)據(jù)上[包裹]這個(gè)函數(shù)名(即相當(dāng)于將后臺(tái)返回來(lái)的數(shù)據(jù)作為函數(shù)的參數(shù),執(zhí)行該函數(shù))改艇,發(fā)送給前端收班。換句話說(shuō),JSONP 需要對(duì)應(yīng)接口的后端的配合才能實(shí)現(xiàn)谒兄。1. 定義數(shù)據(jù)處理函數(shù)_fun 2. 創(chuàng)建script標(biāo)簽摔桦,src的地址執(zhí)行后端接口,最后加個(gè)參數(shù) callback=_fun 3. 服務(wù)端在收到請(qǐng)求后承疲,解析參數(shù)邻耕,計(jì)算返還數(shù)據(jù),輸出 fun(data) 字符串燕鸽。 4. fun(data)會(huì)放到script標(biāo)簽做為js執(zhí)行兄世。此時(shí)會(huì)調(diào)用fun函數(shù),將data做為參數(shù)啊研。
-
CORS
CORS 全稱是跨域資源共享(Cross-Origin Resource Sharing)御滩,是一種 ajax 跨域請(qǐng)求資源的方式,支持現(xiàn)代瀏覽器党远,IE支持10以上削解。
實(shí)現(xiàn)方式:當(dāng)你使用 XMLHttpRequest 發(fā)送請(qǐng)求時(shí),瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略沟娱,會(huì)給該請(qǐng)求加一個(gè)請(qǐng)求頭:Origin氛驮,后臺(tái)進(jìn)行一系列處理,如果確定接受請(qǐng)求則在返回結(jié)果中加入一個(gè)響應(yīng)頭:Access-Control-Allow-Origin; 瀏覽器判斷該相應(yīng)頭中是否包含 Origin 的值济似,如果有則瀏覽器會(huì)處理響應(yīng)柳爽,我們就可以拿到響應(yīng)數(shù)據(jù),如果不包含瀏覽器直接駁回碱屁,這時(shí)我們無(wú)法拿到響應(yīng)數(shù)據(jù)磷脯。所以 CORS 的表象是讓你覺(jué)得它與同源的 ajax 請(qǐng)求沒(méi)啥區(qū)別,代碼完全一樣娩脾。 -
降域
此方案僅限主域相同赵誓,子域不同的跨域應(yīng)用場(chǎng)景。
實(shí)現(xiàn)原理:兩個(gè)頁(yè)面都通過(guò)js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域柿赊,就實(shí)現(xiàn)了同域俩功。<script> document.domain = 'xxx.com' </script>
-
postMessage
window.postMessage() 方法可以安全地實(shí)現(xiàn)跨源通信
從廣義上講,一個(gè)窗口可以獲得對(duì)另一個(gè)窗口的引用(比如targetWindow = window.opener
)碰声,然后在窗口上調(diào)用targetWindow.postMessage()
方法分發(fā)一個(gè)MessageEvent
, MessageEvent 是接口代表一段被目標(biāo)對(duì)象接收的消息诡蜓。")消息。接收消息的窗口可以根據(jù)需要自由處理此事件胰挑。傳遞給 window.postMessage() 的參數(shù)(比如 message )將通過(guò)消息事件對(duì)象暴露給接收消息的窗口
- 用法:postMessage(data,origin,[transfer])方法接受兩個(gè)參數(shù)蔓罚,第三個(gè)參數(shù)可選椿肩。
- data: html5規(guī)范支持任意基本類型或可復(fù)制的對(duì)象
- origin: 協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為
*
豺谈,表示可以傳遞給任意窗口郑象,如果要指定和當(dāng)前窗口同源的話設(shè)置為/
<div class="ct"> <h1>使用postMessage實(shí)現(xiàn)跨域</h1> <div class="main"> <input type="text" placeholder="http://a.jrg.com:8080/a.html"> </div> <iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe> </div> //a.html里的js代碼 <script> $('.main input').addEventListener('input', function(){ console.log(this.value); window.frames[0].postMessage(this.value,'*');//向b.html傳送跨域數(shù)據(jù) }) // 接收b.html返回回來(lái)的數(shù)據(jù) window.addEventListener('message',function(e) { $('.main input').value = e.data console.log(e.data); }); </script> //b.html里的js代碼 <script> //接收a.html的數(shù)據(jù) window.addEventListener('message',function(e) { $('#input').value = e.data console.log(e.data); }); //返回?cái)?shù)據(jù)給a.html $('#input').addEventListener('input', function(){ window.parent.postMessage(this.value, '*'); }) </script>
4. CORS具體的實(shí)現(xiàn)代碼
- 使用
XMLHttpRequest
發(fā)送請(qǐng)求,瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略茬末,會(huì)給該請(qǐng)求加一個(gè)請(qǐng)求頭:Origin
厂榛;后臺(tái)進(jìn)行處理,在返回結(jié)果中加入一個(gè)響應(yīng)頭:Access-Control-Allow-Origin
丽惭;瀏覽器判斷該相應(yīng)頭中是否包含Origin
的值击奶,有則處理響應(yīng),無(wú)則直接駁回 res.setHeader('Access-Control-Allow-Origin','127.0.0.1:8080')
res.setHeader('Access-Control-Allow-Origin','*')
- index.html頁(yè)面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div class="container"> <ul class="news"></ul> <button class="show">show news</button> </div> <script> function $(selector){ return document.querySelector(selector) } function appendHtml(news){ var html = '' for (var i=0; i<news.length; i++){ html += '<li>'+ news[i] + '</li>' } $('.news').innerHTML = html } $('.show').addEventListener('click', function(){ var xhr = new XMLHttpRequest() xhr.open('GET', 'http://127.0.0.1:8080/getNews', true) xhr.onload = function(){ appendHtml(JSON.parse(xhr.responseText)) } xhr.send() }) </script> </body> </html>
- server.js
var http = require('http') var fs = require('fs') var path = require('path') var url = require('url') http.createServer(function(req,res){ var pathObj = url.parse(req.url,true) switch (pathObj.pathname){ case '/getNews': var news = [ "這是第一條消息责掏,獲取了消息一", "這是第二條消息柜砾,獲取了消息二", "這是第三條消息,獲取了消息三" ] res.setHeader('Access-Control-Allow-Origin','127.0.0.1:8080') res.end(JSON.stringify(news)) break; default: fs.readFile(path.join(__dirname,pathObj.pathname),function(e,data){ if(e){ res.writeHead(404,'not found') res.end('<h1>404 Not Found</h1>') } else { res.end(data) } }) } }).listen(8080)
- 啟動(dòng)終端拷橘,執(zhí)行 node server.js
- 瀏覽器打開(kāi)http://localhost:8080/index.html ,查看效果和網(wǎng)絡(luò)請(qǐng)求
- 點(diǎn)擊[show news]按鈕 局义,會(huì)出現(xiàn)下圖報(bào)錯(cuò),因?yàn)楹蠖嗽O(shè)置的
Access-Control-Allow-Origin
是127.0.0.1:8080
- 瀏覽器打開(kāi)http://127.0.0.1:8080/index.html ,點(diǎn)擊[show news]按鈕 冗疮,此時(shí)網(wǎng)頁(yè)能正常響應(yīng)
- 若將
res.setHeader('Access-Control-Allow-Origin','127.0.0.1:8080')
改成res.setHeader('Access-Control-Allow-Origin','*')
萄唇,打開(kāi)服務(wù)器,瀏覽器打開(kāi)http://localhost:8080/index.html,瀏覽器頁(yè)能正常響應(yīng)术幔。*
表示匹配所有的窗口
以上為簡(jiǎn)單為跨域簡(jiǎn)單總結(jié)