1: 什么是同源策略
最初,它的含義是指,A網(wǎng)頁(yè)設(shè)置的 Cookie,B網(wǎng)頁(yè)不能打開(kāi),除非這兩個(gè)網(wǎng)頁(yè)"同源",所謂"同源"指的是"三個(gè)相同".
協(xié)議相同
域名相同
端口相同舉例來(lái)說(shuō):
http://www.example.com/dir/page.html
這個(gè)網(wǎng)址協(xié)議是http://,域名是www.example.com,端口是80(默認(rèn)端口可以省略),它的同源情況如下.
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
https://www.example.com:81/dir/other.html:不同源(https協(xié)議不同)
2: 什么是跨域?跨域有幾種實(shí)現(xiàn)形式
-
跨域出現(xiàn)的原因
JavaScript出于安全方面的考慮,不允許一個(gè)網(wǎng)頁(yè)訪問(wèn)一個(gè)非同源的網(wǎng)頁(yè),即2個(gè)網(wǎng)址的協(xié)議相同,域名相同,端口相同其中任意一個(gè)不同就是非同源. -
概念:只要協(xié)議、域名、端口有任何一個(gè)不同,都被當(dāng)作是不同的域。
比如:有一個(gè)Ajax的var xhr=new XMLHttpRequest()的xhr對(duì)象,在a網(wǎng)址里發(fā)生請(qǐng)求到一個(gè)非同源的b網(wǎng)址,會(huì)請(qǐng)求的報(bào)錯(cuò).

- 跨域就是為了解決這個(gè)問(wèn)題,實(shí)現(xiàn)非同源網(wǎng)頁(yè)之間的數(shù)據(jù)傳輸和通信.
跨域常見(jiàn)方式
JSONP(JSON with Padding 填充式JSON 或參數(shù)式JSON)CORS(Cross-Origin Resource Sharing,跨源資源共享)HTML5的
window.postMessage
window.postMessage(message,targetOrigin) 方法是html5新引進(jìn)的特性,可以使用它來(lái)向其它的window對(duì)象發(fā)送消息,無(wú)論這個(gè)window對(duì)象是屬于同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經(jīng)支持window.postMessage方法。
window.postMessage允許兩個(gè)窗口/幀之間跨域發(fā)送數(shù)據(jù)消息。從本質(zhì)上講,window.postMessage是一個(gè)跨域的無(wú)服務(wù)器墊片的Ajax。降域: document.domain
使用條件:
有其他頁(yè)面 window 對(duì)象的引用,
二級(jí)域名相同,
協(xié)議相同,
端口相同
//在頁(yè)面 http://www.example.com/a.html 中設(shè)置document.domain:
<iframe src="example.com/b.html" id="iframe" onload="test()"></iframe>
<script>
document.domain='example.com';//設(shè)置成主域
function test(){
alert(document.getElementById('iframe').contentWindow);
}
</script>
//在頁(yè)面 http://example.com/b.html中也設(shè)置document.domain
<script>
document.domain='example.com';//在iframe載入的這個(gè)頁(yè)面也設(shè)置 document.domain與主頁(yè)面相同
</script>
//而且是必須的,雖然這個(gè)文檔的domain就是example.com,但是還是必須顯示的設(shè)置document.domain的值
3: JSONP 的原理是什么
JSONP (JSON with Padding)是一個(gè)簡(jiǎn)單高效的跨域方式,html中的script標(biāo)簽可以加載并執(zhí)行其他域的JavaScript,于是我們可以通過(guò)script標(biāo)記來(lái)動(dòng)態(tài)加載其他域的資源,例如我要從域A的頁(yè)面pageA加載域B的數(shù)據(jù),那么在域B的頁(yè)面pageB中我以JavaScript的形式聲明pageA需要的數(shù)據(jù),然后在pageA中用script標(biāo)簽把pageB加載進(jìn)來(lái),那么pageB中的腳本就會(huì)得以執(zhí)行。JSONP在此基礎(chǔ)上加入了回調(diào)函數(shù),pageB加載完之后會(huì)執(zhí)行pageA中定義的函數(shù),所需要的數(shù)據(jù)會(huì)以參數(shù)的形式傳遞給該函數(shù)。JSONP易于實(shí)現(xiàn),但是也會(huì)存在一些安全隱患,如果第三方的腳本隨意地執(zhí)行,那么它就可以篡改頁(yè)面內(nèi)容,截獲敏感數(shù)據(jù)。但是在受信任的雙方傳遞數(shù)據(jù),JSONP是非常合適的選擇。
.css,.js,圖片的引用和jsonp跨域拿到j(luò)s方法有什么區(qū)別 ?
相同點(diǎn):
在html里.js,圖片的引用和jsonp拿到j(luò)s的方法是一樣的,都是從服務(wù)器那到j(luò)s文件然后插入到html里.
不同:
只不過(guò)出發(fā)點(diǎn)是不一樣,前者是加載資源,后者為了跨域拿后臺(tái)返回的js.
jsonp還多了一個(gè)回調(diào),區(qū)別是出發(fā)點(diǎn)和應(yīng)用方法不同,但都有從服務(wù)器返回的資源.
4: CORS是什么
CORS(Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問(wèn)跨域資源時(shí),瀏覽器與服務(wù)器應(yīng)該如何溝通.CORS背后的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進(jìn)行溝通,從而決定請(qǐng)求或響應(yīng)是應(yīng)該成功還是失敗.跨域后瀏覽器不會(huì)返回?cái)?shù)據(jù).
**簡(jiǎn)單請(qǐng)求 **
瀏覽器將CORS請(qǐng)求分成兩類:簡(jiǎn)單請(qǐng)求(simple request)和
簡(jiǎn)單請(qǐng)求條件
1) 請(qǐng)求方法是以下三種方法中的一個(gè):
HEAD
GET
POST
2)HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三個(gè)值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同時(shí)滿足上面兩個(gè)條件,就屬于非簡(jiǎn)單請(qǐng)求
-
使用
在服務(wù)器后臺(tái)設(shè)置header屬性Access-Control-Allow-Origin
它的值是請(qǐng)求時(shí)Origin字段的值或者*,*表示接受任意域名的請(qǐng)求。
app.get('/getNews', function(req, res){
var news = [
"第11日前瞻:中國(guó)沖擊4金 博爾特再戰(zhàn)200米羽球",
]
var data = [];
for(var i=0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index]);
news.splice(index, 1);
}
res.header("Access-Control-Allow-Origin", "http://a.jrg.com:8080"); //代表只接受http://a.jrg.com:8080網(wǎng)址的請(qǐng)求
//res.header("Access-Control-Allow-Origin", "*"); //*表示接受任意域名的請(qǐng)求
res.send(data);
})
-
非簡(jiǎn)單請(qǐng)求
是那種對(duì)服務(wù)器有特殊要求的請(qǐng)求,比如請(qǐng)求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
非簡(jiǎn)單請(qǐng)求的CORS請(qǐng)求,會(huì)在正式通信之前,增加一次HTTP查詢請(qǐng)求,稱為"預(yù)檢"請(qǐng)求(preflight).
瀏覽器先詢問(wèn)服務(wù)器,當(dāng)前網(wǎng)頁(yè)所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些HTTP動(dòng)詞和頭信息字段。只有得到肯定答復(fù),瀏覽器才會(huì)發(fā)出正式的XMLHttpRequest請(qǐng)求,否則就報(bào)錯(cuò)。
-
CORS與JSONP的比較
CORS與JSONP的使用目的相同,但是比JSONP更強(qiáng)大,但CORS不支持IE6.7.8.
JSONP只支持GET請(qǐng)求,CORS支持所有類型的HTTP請(qǐng)求。JSONP的優(yōu)勢(shì)在于支持老式瀏覽器,以及可以向不支持CORS的網(wǎng)站請(qǐng)求數(shù)據(jù).
5: 演示三種常用以上跨域的解決方式
-
jsonp
參考阮一峰
先在客戶端上設(shè)置 新 Hosts
127.0.0.1 a.jrg.com
127.0.0.1 b.jrg.com
127.0.0.1 jrg.com
router.js
app.get('/getNews', function(req, res){
var news = [
'我沒(méi)有特別的才能,只有強(qiáng)烈的好奇心。永遠(yuǎn)保持好奇心的人是永遠(yuǎn)進(jìn)步的人?!獝?ài)因斯坦',
'愛(ài)因斯坦認(rèn)為他之所以取得成功,原因在于他具有狂熱的好奇心.',
'求知欲,好奇心這是人的永恒的,不可改變的特性。哪里沒(méi)有求知欲,哪里便沒(méi)有學(xué)校?!K霍姆林斯基',
'孩子提出的問(wèn)題越多,那么他在童年早期認(rèn)識(shí)周圍的東西也就愈多,在學(xué)校中越聰明,眼睛愈明,記憶力愈敏銳。要培養(yǎng)自己孩子的智力,那你就得教給他思考?!K霍姆林斯基',
'我想起了自己小學(xué)的學(xué)習(xí)經(jīng)歷,終于理解了為什么小時(shí)候成績(jī)好,我那時(shí)候確實(shí)好奇心非常強(qiáng)烈.',
'人的內(nèi)心里有一種根深蒂固的需要——總想感到自己是發(fā)現(xiàn)者、研究者、探尋者。在兒童的精神世界中,這種需求特別強(qiáng)烈。但如果不向這種需求提供養(yǎng)料,即不積極接觸事實(shí)和現(xiàn)象,缺乏認(rèn)識(shí)的樂(lè)趣,這種需求就會(huì)逐漸消失,求知興趣也與之一道熄滅。(蘇霍姆林斯基)',
'生活的全部意義在于無(wú)窮地探索尚未知道的東西,在于不斷地增加更多的知識(shí)?!罄?
]
var data = [];
for(var i=0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index]);
news.splice(index, 1);//把data數(shù)組里已經(jīng)有的元素從news數(shù)組里刪除,保證不重復(fù)上一步拿到的新聞
}
var cb = req.query.callback
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}else{
res.send(data);
}
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container{
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>我沒(méi)有特別的才能,只有強(qiáng)烈的好奇心 ——愛(ài)因斯坦</li>
<li>我沒(méi)有特別的才能,只有強(qiáng)烈的好奇心 ——愛(ài)因斯坦</li>
<li>我沒(méi)有特別的才能,只有強(qiáng)烈的好奇心 ——愛(ài)因斯坦</li>
</ul>
<button class="change">點(diǎn)我換一組</button>
</div>
<script>
$('.change').addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml';//必須是'http://xxx.com:8080/的形式
document.head.appendChild(script);
document.head.removeChild(script);
})
function appendHtml(news){
var html = '';
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id){//$函數(shù),發(fā)請(qǐng)求前和點(diǎn)擊換一組都調(diào)用.傳入?yún)?shù),直接返回.
return document.querySelector(id);//替換$(id)為document.querySelector(id),因?yàn)闉g覽器不支持jquery庫(kù)
}
</script>
</body>
</html>
結(jié)合上面的案例說(shuō)下jsonp的本質(zhì):
后臺(tái)判斷:
var cb = req.query.callback
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}
場(chǎng)景:
A網(wǎng)訪問(wèn)跨域的B網(wǎng)資源:
在A網(wǎng)通過(guò)給script標(biāo)簽的src賦值一個(gè)網(wǎng)址.
例子: var script = document.createElement('script');
script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml'
這個(gè)網(wǎng)址帶有回調(diào)的方法名.加載script腳本到B網(wǎng)頁(yè),B網(wǎng)頁(yè)發(fā)回消息去調(diào)用在A網(wǎng)頁(yè)腳本里定義的回調(diào)函數(shù).callback.
6.CORS案例

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container{
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>CORS練習(xí)</li>
<li>男雙力爭(zhēng)會(huì)師決賽 </li>
<li>女排將死磕巴西!</li>
</ul>
<button class="change">換一組</button>
</div>
<script>
$('.change').addEventListener('click', function(){
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://b.jrg.com:8080/getNews', true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
appendHtml( JSON.parse(xhr.responseText) )
}
}
window.xhr = xhr//why?
})
function appendHtml(news){
var html = '';
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id){
return document.querySelector(id);
}
</script>
</html>
router.js
app.get('/getNews', function(req, res){
var news = [
"第11日前瞻:中國(guó)沖擊4金 博爾特再戰(zhàn)200米羽球",
"正直播柴飚/洪煒出戰(zhàn) 男雙力爭(zhēng)會(huì)師決賽",
"女排將死磕巴西!郎平安排男陪練模仿對(duì)方核心",
"沒(méi)有中國(guó)選手和巨星的110米欄 我們還看嗎?",
"中英上演奧運(yùn)金牌大戰(zhàn)",
"博彩賠率挺中國(guó)奪回第二紐約時(shí)報(bào):中國(guó)因?qū)κ址幎鴣G失的獎(jiǎng)牌最多",
"最“出柜”奧運(yùn)?同性之愛(ài)閃耀里約",
"下跪拜謝與洪荒之力一樣 都是真情流露"
]
var data = [];
for(var i=0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index]);
news.splice(index, 1);
}
res.header("Access-Control-Allow-Origin", "http://jrg.com:8080");
//res.header("Access-Control-Allow-Origin", "*");
res.send(data);
7.document.domain降域

情景:a.html里面嵌入iframe元素,且這個(gè)iframe是<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>,而b.html就是個(gè)其中src規(guī)定顯示在 iframe 中的文檔的地址,也是絕對(duì) URL - 指向其他站點(diǎn)(比如 src="www.example.com/index.html"),這里是個(gè)非同源的b.html
//a.html文件
<html>
<style>
.ct{
width: 910px;
margin: auto;
}
.main{
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input{
margin: 20px;
width: 200px;
}
.iframe{
float: right;
}
iframe{
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用降域?qū)崿F(xiàn)跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
//URL: http://a.jrg.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "jrg.com"http://降域關(guān)鍵代碼
</script>
</html>
//b.html
<html>
<style>
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
document.querySelector('#input').addEventListener('input', function(){
window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'jrg.com';
</script>
</html>
- window.frames[0].postMessage
//a.html文件
<html>
<style>
.ct{
width: 910px;
margin: auto;
}
.main{
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input{
margin: 20px;
width: 200px;
}
.iframe{
float: right;
}
iframe{
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<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>
<script>
//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].postMessage(this.value,'*');//window.frames[0]是window子窗口的第一個(gè).*代表任意地址
})
window.addEventListener('message',function(e) {
$('.main input').value = e.data
console.log(e.data);
});
//
window.addEventListener('message',function(e){
$('.mian input').value = e.data;
})
function $(id){
return document.querySelector(id);
}
</script>
</html>
//b.html文件:即被內(nèi)嵌的iframe鏈接文檔url.
<html>
<style>
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
//傳出數(shù)據(jù)到內(nèi)嵌此窗口的父窗口即a.html.
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*');//
//返回當(dāng)前窗口的父窗口對(duì)象.如果一個(gè)窗口沒(méi)有父窗口,則它的 parent 屬性為自身的引用.
//如果當(dāng)前窗口是一個(gè) <iframe>, <object>, 或者 <frame>,則它的父窗口是嵌入它的那個(gè)窗口
})
//接收信息
window.addEventListener('message',function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
</html>
