在了解跨域之前,我們先了解下什么是同源策略
1.什么是同源策略?
同源策略/SOP(Same origin policy)是一種約定,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源是指"協(xié)議+域名+端口"三者相同,即便兩個不同的域名指向同一個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 (端口不同,第一個是80)
需要注意的是: 對于當(dāng)前頁面來說頁面存放的 JS 文件的域不重要,重要的是加載該 JS 頁面所在什么域
2.什么是跨域?
瀏覽器從一個域名的網(wǎng)頁去請求另一個域名的資源時,域名、端口、協(xié)議任一不同,都是跨域
3.跨域的幾種解決方式
-
使用JSONP方式實(shí)現(xiàn)跨域:
原理:JSONP是通過script標(biāo)簽加載數(shù)據(jù)的方式去獲取數(shù)據(jù)當(dāng)做JS代碼來執(zhí)行。具體操作方法:提前在頁面上聲明一個函數(shù)(這個函數(shù)用來處理后端返回的數(shù)據(jù)),函數(shù)名通過接口的方式傳遞給后臺;后臺解析到函數(shù)名后在原始數(shù)據(jù)上[包裹]這個函數(shù)名(即相當(dāng)于將后臺返回來的數(shù)據(jù)作為函數(shù)的參數(shù),執(zhí)行該函數(shù)),發(fā)送給前端。換句話說,JSONP 需要對應(yīng)接口的后端的配合才能實(shí)現(xiàn)。1. 定義數(shù)據(jù)處理函數(shù)_fun 2. 創(chuàng)建script標(biāo)簽,src的地址執(zhí)行后端接口,最后加個參數(shù) callback=_fun 3. 服務(wù)端在收到請求后,解析參數(shù),計(jì)算返還數(shù)據(jù),輸出 fun(data) 字符串。 4. fun(data)會放到script標(biāo)簽做為js執(zhí)行。此時會調(diào)用fun函數(shù),將data做為參數(shù)。 -
CORS
CORS 全稱是跨域資源共享(Cross-Origin Resource Sharing),是一種 ajax 跨域請求資源的方式,支持現(xiàn)代瀏覽器,IE支持10以上。
實(shí)現(xiàn)方式:當(dāng)你使用 XMLHttpRequest 發(fā)送請求時,瀏覽器發(fā)現(xiàn)該請求不符合同源策略,會給該請求加一個請求頭:Origin,后臺進(jìn)行一系列處理,如果確定接受請求則在返回結(jié)果中加入一個響應(yīng)頭:Access-Control-Allow-Origin; 瀏覽器判斷該相應(yīng)頭中是否包含 Origin 的值,如果有則瀏覽器會處理響應(yīng),我們就可以拿到響應(yīng)數(shù)據(jù),如果不包含瀏覽器直接駁回,這時我們無法拿到響應(yīng)數(shù)據(jù)。所以 CORS 的表象是讓你覺得它與同源的 ajax 請求沒啥區(qū)別,代碼完全一樣。 -
降域
此方案僅限主域相同,子域不同的跨域應(yīng)用場景。
實(shí)現(xiàn)原理:兩個頁面都通過js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域,就實(shí)現(xiàn)了同域。<script> document.domain = 'xxx.com' </script> -
postMessage
window.postMessage() 方法可以安全地實(shí)現(xiàn)跨源通信
從廣義上講,一個窗口可以獲得對另一個窗口的引用(比如targetWindow = window.opener),然后在窗口上調(diào)用targetWindow.postMessage()方法分發(fā)一個MessageEvent, MessageEvent 是接口代表一段被目標(biāo)對象接收的消息。")消息。接收消息的窗口可以根據(jù)需要自由處理此事件。傳遞給 window.postMessage() 的參數(shù)(比如 message )將通過消息事件對象暴露給接收消息的窗口
- 用法:postMessage(data,origin,[transfer])方法接受兩個參數(shù),第三個參數(shù)可選。
- data: html5規(guī)范支持任意基本類型或可復(fù)制的對象
- origin: 協(xié)議+主機(jī)+端口號,也可以設(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返回回來的數(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ā)送請求,瀏覽器發(fā)現(xiàn)該請求不符合同源策略,會給該請求加一個請求頭:Origin;后臺進(jìn)行處理,在返回結(jié)果中加入一個響應(yīng)頭:Access-Control-Allow-Origin;瀏覽器判斷該相應(yīng)頭中是否包含Origin的值,有則處理響應(yīng),無則直接駁回 res.setHeader('Access-Control-Allow-Origin','127.0.0.1:8080')res.setHeader('Access-Control-Allow-Origin','*')- index.html頁面
<!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) - 啟動終端,執(zhí)行 node server.js
啟動服務(wù)器.PNG - 瀏覽器打開http://localhost:8080/index.html ,查看效果和網(wǎng)絡(luò)請求
localhost.PNG - 點(diǎn)擊[show news]按鈕 ,會出現(xiàn)下圖報(bào)錯,因?yàn)楹蠖嗽O(shè)置的
Access-Control-Allow-Origin是127.0.0.1:8080wrong1.PNG - 瀏覽器打開http://127.0.0.1:8080/index.html ,點(diǎn)擊[show news]按鈕 ,此時網(wǎng)頁能正常響應(yīng)
ok1.PNG - 若將
res.setHeader('Access-Control-Allow-Origin','127.0.0.1:8080')
改成res.setHeader('Access-Control-Allow-Origin','*'),打開服務(wù)器,瀏覽器打開http://localhost:8080/index.html,瀏覽器頁能正常響應(yīng)。*表示匹配所有的窗口
ok2.PNG
以上為簡單為跨域簡單總結(jié)