同源&跨域(JSONP,CORS葫录,降域着裹,PostMessage)

同源策略

瀏覽器出于安全方面的考慮,只允許與本域下的接口交互米同。不同源的客戶端腳本在沒有明確授權(quán)的情況下骇扇,不能讀寫對方的資源。

本域指的是面粮?

同源:

不同源:

需要注意的是: 對于當(dāng)前頁面來說頁面存放的 JS 文件的域不重要,重要的是加載該 JS 頁面所在什么域

什么是跨域熬苍?

跨域就是Ajax請求的地址與當(dāng)前頁面的url不是同源稍走,即要么不同協(xié)議,要么不同域名柴底,要么不同端口婿脸。
舉個例子, 這是一個簡單的Ajax:請求的地址是http://127.0.0.1:8080/getWeather柄驻,

<h1>hello world</h1>
<script>
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'http://127.0.0.1:8080/getWeather', true)
    xhr.send()
    xhr.onload = function(){
        console.log(xhr.responseText)
    }
</script>

這是一個簡單的服務(wù)器狐树,支持靜態(tài)頁面和路由:

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)
    console.log(pathObj)

    switch (pathObj.pathname) {
        case '/getWeather':
            res.end(JSON.stringify({beijing: 'sunny'}))
            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)

這時打開服務(wù)器,運(yùn)行js鸿脓,輸入url:http://localhost:8080/index.html抑钟,會出現(xiàn)如下效果:

image.png

這就是跨域,由于請求的地址是http://127.0.0.1:8080/getWeather野哭,而當(dāng)前的頁面是http://localhost:8080/index.html在塔,域名不同,就無法請求到數(shù)據(jù)拨黔。

那跨域是如何產(chǎn)生的蛔溃?

有幾種情況:1.請求根本沒有發(fā)出去,直接被瀏覽器攔截掉了蓉驹。2.請求發(fā)出去了,但服務(wù)器發(fā)現(xiàn)不一樣揪利,服務(wù)器沒有響應(yīng)态兴。3.請求發(fā)出去了,服務(wù)器也響應(yīng)了疟位,但數(shù)據(jù)返回來之后瞻润,瀏覽器發(fā)現(xiàn)不是同源,給攔截掉了。

于是我們給js文件里面加一句話绍撞,如果請求出現(xiàn)了又執(zhí)行發(fā)送了正勒,便是第三種情況。


image.png

這時我們重啟服務(wù)器傻铣,刷新頁面章贞,發(fā)現(xiàn)終端有了這句話:


image.png

也就是說,請求發(fā)出去后非洲,服務(wù)器收到了鸭限,可是瀏覽器不接受,因為瀏覽器覺得數(shù)據(jù)不安全两踏,換句話說败京,同源和跨域是瀏覽器的安全機(jī)制。

跨域的幾種實現(xiàn)方式

JSONP

因為Ajax直接請求普通文件存在跨域無權(quán)限訪問的問題梦染,不管是靜態(tài)頁面赡麦、動態(tài)網(wǎng)頁、web服務(wù)帕识、WCF泛粹,只要是跨域請求,一律不準(zhǔn)渡冻。

不過我們Web頁面上調(diào)用js文件時則不受是否跨域的影響戚扳,不僅如此,凡是擁有”src”這個屬性的標(biāo)簽都擁有跨域的能力族吻,比如<\script>帽借、<\img>、<\iframe>

那跟JSON又有什么關(guān)系呢超歌?
因為JSON的純字符數(shù)據(jù)格式可以簡潔的描述復(fù)雜數(shù)據(jù)砍艾,更妙的是JSON還被js原生支持,所以在客戶端幾乎可以隨心所欲的處理這種格式的數(shù)據(jù)巍举。

根據(jù)以上講述脆荷,可以寫一個簡單的JSONP:

<script>
function showData(ret){
console.log(ret);
}
</script>
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>

之前后端返回數(shù)據(jù): {"city": "hangzhou", "weather": "晴天"} 現(xiàn)在后端返回數(shù)據(jù): showData({"city": "hangzhou", "weather": "晴天"}) 前端script標(biāo)簽在加載數(shù)據(jù)后會把 「showData({“city”: “hangzhou”, “weather”: “晴天”})」做為 js 來執(zhí)行,這實際上就是調(diào)用showData這個函數(shù)懊悯,同時參數(shù)是 {“city”: “hangzhou”, “weather”: “晴天”}蜓谋。 用戶只需要在加載提前在頁面定義好showData這個全局函數(shù),在函數(shù)內(nèi)部處理參數(shù)即可炭分。

下面看一個具體的:
index.html

<!DOCTYPE html>
<html>
<body>
  <div class="container">
    <ul class="news">
    </ul>
    <button class="show">show news</button>
  </div>

<script>

  $('.show').addEventListener('click', function(){
    var script = document.createElement('script');
    script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
    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){
    return document.querySelector(id);
  }
</script>

</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 = [
        "第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球",
        "正直播柴飚/洪煒出戰(zhàn) 男雙力爭會師決賽",
        "女排將死磕巴西桃焕!郎平安排男陪練模仿對方核心"
        ]
      res.setHeader('Content-Type','text/json; charset=utf-8')
      if(pathObj.query.callback){
        res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')')
      }else{
        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)

效果如下:

[圖片上傳失敗...(image-63fd88-1551773332301)]
點擊后:

[圖片上傳失敗...(image-1b22a7-1551773332302)]
這里我們請求的是:http://127.0.0.1:8080/getNews?callback=appendHtml,但頁面url是http://localhost:8080/index.html捧毛,不同源也能夠請求數(shù)據(jù)观堂。
因為后端做了判斷:
[圖片上傳失敗...(image-d59de2-1551773332302)]
返回的是

appendHtml(["第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球","正直播柴飚/洪煒出戰(zhàn) 男雙力爭會師決賽","女排將死磕巴西让网!郎平安排男陪練模仿對方核心"])

然后這個appenHtml函數(shù)再到html文件中執(zhí)行:

  function appendHtml(news){
    var html = '';
    for( var i=0; i<news.length; i++){
      html += '<li>' + news[i] + '</li>';
    }
    console.log(html);
    $('.news').innerHTML = html;
  }

最后就是我們呈現(xiàn)的效果。

CORS

CORS(Cross-Origin Resource Sharing)

全名:跨域資源共享师痕。是一種 ajax 跨域請求資源的方式溃睹,支持現(xiàn)代瀏覽器,IE支持10以上胰坟。 實現(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ū)別,代碼完全一樣俗冻。

什么意思呢礁叔?看如下代碼:
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 = [
        "第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球",
        "正直播柴飚/洪煒出戰(zhàn) 男雙力爭會師決賽",
        "女排將死磕巴西!郎平安排男陪練模仿對方核心"
        ]

      res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')
      //res.setHeader('Access-Control-Allow-Origin','*')
      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)

index.html

<!DOCTYPE html>
<html>
<body>
  <div class="container">
    <ul class="news">

    </ul>
    <button class="show">show news</button>
  </div>

<script>

  $('.show').addEventListener('click', function(){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'http://127.0.0.1:8080/getNews', true)
    xhr.send()
    xhr.onload = function(){
      appendHtml(JSON.parse(xhr.responseText))
    }
  })

  function appendHtml(news){
    var html = ''
    for( var i=0; i<news.length; i++){
      html += '<li>' + news[i] + '</li>'
    }
    $('.news').innerHTML = html
  }

  function $(selector){
    return document.querySelector(selector)
  }
</script>
</html>

可以看出html文件與Ajax沒有區(qū)別迄薄,只不過在js文件中加了一句話:

 res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')

打開服務(wù)器琅关,輸入網(wǎng)址:http://localhost:8080/index.html,效果如下:
[圖片上傳失敗...(image-b32bb5-1551773332302)]
也實現(xiàn)了跨域讥蔽。需要注意的是圖中紅色框的兩個地方涣易。

為了方便理解再看下面這張圖:

[圖片上傳失敗...(image-f05302-1551773332302)]

這就與上方的那段話對應(yīng)上了:

  • 當(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ù),如果不包含瀏覽器直接駁回

降域

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"


</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>

//URL: http://b.jrg.com:8080/b.html
 
document.querySelector('#input').addEventListener('input', function(){
    window.parent.document.querySelector('input').value = this.value;
})

document.domain = 'jrg.com';

</script>
</html>

我們要進(jìn)行的操作是:在http://a.jrg.com/a.html 頁面請求 http://b.jrg.com/b.html芋类。
效果如圖:

image.png

不過需要注意的是在a.jrg.com不能夠調(diào)用ifame隆嗅,因為iframes里面是b.jrg.com。


image.png

但是在b.jrg.com可以:


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侯繁,一起剝皮案震驚了整個濱河市胖喳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巫击,老刑警劉巖禀晓,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坝锰,居然都是意外死亡粹懒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門顷级,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篓吁,“玉大人篡诽,你說我怎么就攤上這事。” “怎么了寂拆?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摊阀。 經(jīng)常有香客問我瓦阐,道長,這世上最難降的妖魔是什么纤子? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任搬瑰,我火速辦了婚禮,結(jié)果婚禮上控硼,老公的妹妹穿的比我還像新娘泽论。我一直安慰自己,他們只是感情好卡乾,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布翼悴。 她就那樣靜靜地躺著,像睡著了一般幔妨。 火紅的嫁衣襯著肌膚如雪鹦赎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天陶冷,我揣著相機(jī)與錄音钙姊,去河邊找鬼。 笑死埂伦,一個胖子當(dāng)著我的面吹牛煞额,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沾谜,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼膊毁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了基跑?” 一聲冷哼從身側(cè)響起婚温,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎媳否,沒想到半個月后栅螟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荆秦,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年力图,在試婚紗的時候發(fā)現(xiàn)自己被綠了步绸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡吃媒,死狀恐怖瓤介,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赘那,我是刑警寧澤刑桑,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站募舟,受9級特大地震影響祠斧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拱礁,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一梁肿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧觅彰,春花似錦吩蔑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至飒责,卻和暖如春赘娄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宏蛉。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工遣臼, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拾并。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓揍堰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嗅义。 傳聞我的和親對象是個殘疾皇子屏歹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容