同源策略和跨域方法

什么是同源策略?

  1. 概念:
同源策略乃秀,它是由[Netscape]提出的一個著名的[安全策略]現(xiàn)在所有支持JavaScript 的瀏覽器都會使用這個策略何什。
所謂同源是指狸演,域名,協(xié)議棍弄,端口相同檬嘀。
當(dāng)一個瀏覽器的兩個tab頁中分別打開來 百度和谷歌的頁面
當(dāng)瀏覽器的百度tab頁執(zhí)行一個腳本的時候會檢查這個腳本是屬于哪個頁面的脊阴,
即檢查是否同源握侧,只有和百度同源的腳本才會被執(zhí)行。
如果非同源嘿期,那么在請求數(shù)據(jù)時品擎,瀏覽器會在控制臺中報一個異常,提示拒絕訪問备徐。

本域所指:

不同源例子:

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

2.什么是跨域?跨域有幾種實現(xiàn)的形式蜜猾?

跨域顧名思義就是突破同源策略的限制盲再,去不同的域下訪問數(shù)據(jù)西设。 主要有如下幾種實現(xiàn)形式:

  • jsonp
  • CORS:跨域資源共享(Cross-Origin Resource Sharing)
  • 降域
  • postMessage()

JSONP的原理

原理:就是利用<script>標簽沒有跨域限制來達到與第三方通訊的目的。當(dāng)需要通訊時答朋,本站腳本創(chuàng)建一個<script>元素,地址指向第三方的API網(wǎng)址棠笑,形如: 
 <script src="http://www.example.net/api?param1=1&param2=2"></script> 
 并提供一個回調(diào)函數(shù)來接收數(shù)據(jù)(函數(shù)名可約定梦碗,或通過地址參數(shù)傳遞)。 
 第三方產(chǎn)生的響應(yīng)為json數(shù)據(jù)的包裝(故稱之為jsonp蓖救,即json padding)洪规,形如: 
 callback({"name":"hax","gender":"Male"}) 
 這樣瀏覽器會調(diào)用callback函數(shù),并傳遞解析后json對象作為參數(shù)循捺。本站腳本可在callback函數(shù)里處理所傳入的數(shù)據(jù)斩例。 

host配置:

QQ截圖20170317170721.jpg
<!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>JSONp</title>
</head>

<body>
    <div id="contaniner">
        <ul id="news">
            <li>第一條新聞</li>
            <li>第二條新聞</li>
            <li>第三條新聞</li>
        </ul>
        <button id="change">更換</button>
    </div>
    <script>
        function $(id) {
            return document.querySelector(id);
        }

        $('#change').addEventListener('click', function () {
            var script = document.createElement('script');//
            script.src ='http://zyn.com:8080/getNews?callback=appendHtml';//地址指向第三方的API網(wǎng)址,供一個回調(diào)函數(shù)來接收數(shù)據(jù)(函數(shù)名可約定,或通過地址參數(shù)傳遞)从橘。
            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;
        }
    </script>
</body>

</html>
//JSONP是一種非正式傳輸協(xié)議念赶,該協(xié)議的一個要點就是允許用戶傳遞一個callback參數(shù)給服務(wù)端,然后服務(wù)端返回數(shù)據(jù)時會將這個callback參數(shù)作為函數(shù)名來包裹住JSON數(shù)據(jù)恰力,這樣客戶端就可以隨意定制自己的函數(shù)來自動處理返回數(shù)據(jù)了叉谜。
//后端

app.get('/getNews',function(req,res){
    
    var news= [
        "第四條新聞",
        "第五條新聞",
        "第六條新聞",
        "第七條新聞",
        "第八條新聞",
        "第九條新聞",
        "第十條新聞",
        "第十一條新聞",
        "第十二條新聞",
        "第十三條新聞"
    ]
    
    var data = [];
    
    for(var i=0; i<3;i++){
        var index = parseInt(Math.random()*news.length) //隨機生成news長度范圍內(nèi)的數(shù)
        data.push(news[index]);//把隨機生成的news的第某項添加到data
        news.splice(index,1);//刪除被添加過得news的項
    }

    var cb = req.query.callback;
    if(cb){
            res.send(cb +'('+ JSON.stringify(data) +')')//callback參數(shù)作為函數(shù)名來包裹住JSON數(shù)據(jù)
    }else{
        res.send(data)
    }
})

20170316_202159.gif

注意:JSONP只支持get請求. CORS支持所有類型的HTTP請求。JSONP的優(yōu)勢在于支持老式瀏覽器踩萎,以及可以向不支持CORS的網(wǎng)站請求數(shù)據(jù)停局。

CORS的原理

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)香府。
它允許瀏覽器向跨源服務(wù)器董栽,發(fā)出[XMLHttpRequest]請求,從而克服了AJAX只能[同源]使用的限制企孩。
CORS簡介:
CORS需要瀏覽器和服務(wù)器同時支持锭碳。目前,所有瀏覽器都支持該功能柠硕,IE瀏覽器不能低于IE10工禾。
整個CORS通信過程,都是瀏覽器自動完成蝗柔,不需要用戶參與闻葵。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別癣丧,代碼完全一樣槽畔。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附加的頭信息胁编,有時還會多出一次附加的請求厢钧,但用戶不會有感覺鳞尔。
因此,實現(xiàn)CORS通信的關(guān)鍵是服務(wù)器早直。只要服務(wù)器實現(xiàn)了CORS接口寥假,就可以跨源通信。
CORS的兩種請求:

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)霞扬。
只要同時滿足以下兩大條件糕韧,就屬于簡單請求。

(1) 請求方法是以下三種方法之一:

          HEAD
          GET
          POST
(2)HTTP的頭信息不超出以下幾種字段:
          Accept
         Accept-Language
         Content-Language
         Last-Event-ID
         Content-Type:只限于三個值application/x-www-form-urlencoded喻圃、multipart/form-data萤彩、text/plain
凡是不同時滿足上面兩個條件,就屬于非簡單請求斧拍。
瀏覽器對這兩種請求的處理雀扶,是不一樣的。
  1. 簡單請求:對于簡單請求肆汹,瀏覽器直接發(fā)出CORS請求愚墓。具體來說,就是在頭信息之中县踢,增加一個Origin字段转绷。
QQ截圖20170317165152.jpg

Origin字段用來說明,本次請求來自哪個源(協(xié)議 + 域名 + 端口)硼啤。服務(wù)器根據(jù)這個值议经,決定是否同意這次請求。
如果Origin指定的源谴返,不在許可范圍內(nèi)煞肾,服務(wù)器會返回一個正常的HTTP回應(yīng)。瀏覽器發(fā)現(xiàn)嗓袱,這個回應(yīng)的頭信息沒有包含Access-Control-Allow-Origin字段籍救,就知道出錯了,從而拋出一個錯誤渠抹,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲蝙昙。注意,這種錯誤無法通過狀態(tài)碼識別梧却,因為HTTP回應(yīng)的狀態(tài)碼有可能是200奇颠。

如果Origin指定的域名在許可范圍內(nèi),服務(wù)器返回的響應(yīng)放航,會多出幾個頭信息字段烈拒。

QQ截圖20170317165424.jpg

(1)Access-Control-Allow-Origin
該字段是必須的。它的值要么是請求時Origin字段的值,要么是一個*荆几,表示接受任意域名的請求吓妆。
(2)Access-Control-Allow-Credentials
該字段可選。它的值是一個布爾值吨铸,表示是否允許發(fā)送Cookie行拢。默認情況下上岗,Cookie不包括在CORS請求之中丑蛤。設(shè)為true瓶竭,即表示服務(wù)器明確許可兔辅,Cookie可以包含在請求中,一起發(fā)給服務(wù)器客给。這個值也只能設(shè)為true,如果服務(wù)器不要瀏覽器發(fā)送Cookie,刪除該字段即可握巢。
(3)Access-Control-Expose-Headers
該字段可選。CORS請求時松却,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control暴浦、Content-Language、Content-Type晓锻、Expires歌焦、Last-Modified、Pragma砚哆。如果想拿到其他字段独撇,就必須在Access-Control-Expose-Headers里面指定。如:getResponseHeader('FooBar')可以返回FooBar字段的值躁锁。

上面說到纷铣,CORS請求默認不發(fā)送Cookie和HTTP認證信息。如果要把Cookie發(fā)到服務(wù)器战转,一方面要服務(wù)器同意搜立,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true
另一方面槐秧,開發(fā)者必須在AJAX請求中打開withCredentials屬性啄踊。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
否則,即使服務(wù)器同意發(fā)送Cookie刁标,瀏覽器也不會發(fā)送颠通。或者命雀,服務(wù)器要求設(shè)置Cookie蒜哀,瀏覽器也不會處理。
但是,如果省略withCredentials設(shè)置撵儿,有的瀏覽器還是會一起發(fā)送Cookie乘客。這時,可以顯式關(guān)閉withCredentials淀歇。

xhr.withCredentials = false;
需要注意的是易核,如果要發(fā)送Cookie,Access-Control-Allow-Origin就不能設(shè)為星號浪默,必須指定明確的牡直、與請求網(wǎng)頁一致的域名。同時纳决,Cookie依然遵循同源政策碰逸,只有用服務(wù)器域名設(shè)置的Cookie才會上傳,其他域名的Cookie并不會上傳阔加,且(跨源)原網(wǎng)頁代碼中的document.cookie也無法讀取服務(wù)器域名下的Cookie

  1. 非簡單請求:非簡單請求是那種對服務(wù)器有特殊要求的請求饵史,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json胜榔。
    在此不再介紹請參考阮一峰非簡單請求
<!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>CORS</title>
    <style>
        .container{
             width:900px;
             margin:0 auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <ul class="news">
            <li>頭條新聞1</li>
            <li>頭條新聞2</li>
            <li>頭條新聞3</li>
            <li>頭條新聞4</li>
        </ul>
        <button class="change">換一組</button>
    </div>

    <script>
        function $(id){
            return document.querySelector(id);
        }

        $('.change').addEventListener('click',function(){

            var xhr = new XMLHttpRequest();
            xhr.open('get','http://a.zyn.com:8080/getNews',true);
            //html通過zyn.com打開而通過ajax獲取a.zyn.com上的資源胳喷,所以為跨域。
            xhr.send();
            xhr.onreadystatechange = function(){
                
                if(xhr.readyState ===4 && xhr.status ===200){
                    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;
        }
    </script>
</body>
</html>
//后端
app.get('/getNews',function(req,res){

    var news = [
        "頭條新聞5",
        "頭條新聞6",
        "頭條新聞7",
        "頭條新聞8",
        "頭條新聞9",
        "頭條新聞10",
        "頭條新聞11",
        "頭條新聞12"

    ]
    
     var data =[];
     for(var i=0;i<4;i++){

         var index = parseInt(Math.random()*news.length);
         data.push(news[index]);
         news.splice(index,1);
     }
     res.header("Access-Control-Allow-Origin","http://zyn.com:8080")//表示這個后臺接受來自zyn.com的請求
     //res.header("Access-Control-Allow-Origin","*")//表示這個后臺接受來自任意*的請求
     res.send(data)
    
})
20170317_170355.gif

降域的原理

利用域名為同一個基礎(chǔ)域名("http://a.zyn.com/a.html"和'"http://b.zyn.com/b.html") 使用document.domain="zyn.com"進行跨域夭织;
這兩個域名必須屬于同一個基礎(chǔ)域名!而且所用的協(xié)議吭露,端口都要一致,否則無法利用document.domain進行跨域

<!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>jy</title>
    <style>
        .ct{
            width:910px;
            margin: auto;
        }
        .main{
            float: left;
            width:450px;
            height: 300px;
            border: 1px solid #ccc;
        }
        input{

            margin: 20px;
        }
        iframe{
            width:450px;
            height: 300px;
            border: 1px dashed #ccc;
            float:right
        }
    </style>
</head>
<body>
    <div class="ct">
        <h1>使用降域?qū)崿F(xiàn)跨域</h1>
        <div class="main">
            <input type="text" placeholder="http://a.zyn.com:8080/a.html">
        </div>
        <iframe src="http://b.zyn.com:8080/b.html" frameborder="0"></iframe>

        <!--//此時是在a.zyn.com下請求b.zyn.com下的資源為跨域-->
    </div>
    
    <script>
        var input = document.querySelector('.main input');
        input.addEventListener('input',function(){
            window.frames[0].document.querySelector('input').value =this.value
        })

        document.domain ="zyn.com"http://降域把a.zyn.com 降域為zyn.com 
    </script>
</body>
</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>jy</title>
    <style>
        html,body{
            margin: 0px;
        }
        #input{
            margin: 20px;
            width:200px;
        }
    </style>
</head>
<body>
    <input id="input" type="text" placeholder="b.zyn.com:8080/b.html">
</body>
<script>
    var input = document.querySelector('#input');
    input.addEventListener('input',function(){
        window.parent.document.querySelector('input').value=this.value
    })
    document.domain = 'zyn.com';////降域把b.zyn.com 降域為zyn.com 
</script>
</html>

20170317_175617.gif

postMessage()的原理

在HTML5中新增了postMessage方法尊惰,postMessage可以實現(xiàn)跨文檔消息傳輸(Cross Document Messaging)讲竿,Internet Explorer 8, Firefox 3, Opera 9, Chrome 3和 Safari 4都支持postMessage。
該方法可以通過綁定window的message事件來監(jiān)聽發(fā)送跨文檔消息傳輸內(nèi)容择浊。
用法:
otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow:
其他窗口的一個引用戴卜,比如iframe的contentWindow屬性、執(zhí)行[window.open]返回的窗口對象琢岩、或者是命名過或數(shù)值索引的[window.frames]


message:
將要發(fā)送到其他 window的數(shù)據(jù)投剥。它將會被[結(jié)構(gòu)化克隆算法序列化。這意味著你可以不受什么限制的將數(shù)據(jù)對象安全的傳送給目標窗口而無需自己序列化担孔。


targetOrigin:
通過窗口的origin屬性來指定哪些窗口能接收到消息事件江锨,其值可以是字符串"*"(表示無限制)或者一個URI。在發(fā)送消息的時候糕篇,如果目標窗口的協(xié)議啄育、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發(fā)送拌消;只有三者完全匹配挑豌,消息才會被發(fā)送。這個機制用來控制消息可以發(fā)送到哪些窗口;例如氓英,當(dāng)用
postMessage傳送密碼時侯勉,這個參數(shù)就顯得尤為重要,必須保證它的值與這條包含密碼的信息的預(yù)期接受者的orign屬性完全一致铝阐,來防止密碼被惡意的第三方截獲址貌。如果你明確的知道消息應(yīng)該發(fā)送到哪個窗口,那么請始終提供一個有確切值的targetOrigin徘键,而不是*练对。


transfer:
 可選,是一串和message 同時傳遞的對象. 這些對象的所有權(quán)將被轉(zhuǎn)移給消息的接收方吹害,而發(fā)送一方將不再保有所有權(quán)螟凭。
CTYPE 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>postMessage</title>
    <style>
        .ct{
            width:910px;
            margin: auto;
        }
        .main{
            float: left;
            width:450px;
            height: 300px;
            border: 1px solid #ccc;
        }
        input{

            margin: 20px;
        }
        iframe{
            width:450px;
            height: 300px;
            border: 1px dashed #ccc;
            float:right
        }
    </style>
</head>
<body>
    <div class="ct">
        <h1>postMessage</h1>
        <div class="main">
            <input type="text" placeholder="http://a.zyn.com:8080/a.html">
        </div>
        <iframe src="http://b.zyn.com:8080/b.html" frameborder="0"></iframe>

        <!--//此時是在a.zyn.com下請求b.zyn.com下的資源為跨域-->
    </div>
    
    <script>
        var input = document.querySelector('.main input');
        input.addEventListener('input',function(){
            window.frames[0].postMessage(this.value,'*');
        })

       window.addEventListener('message',function(e){
           input.value =e.data
            console.log(e.data);
       })
    </script>
</body>
</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>postMessage</title>
    <style>
        html,body{
            margin: 0px;
        }
        #input{
            margin: 20px;
            width:200px;
        }
    </style>
</head>
<body>
    <input id="input" type="text" placeholder="http://b.zyn.com:8080/b.html">
</body>
<script>
    var input = document.querySelector('#input');
    input.addEventListener('input',function(){
        window.parent.postMessage(this.value,'*')
    })
   window.addEventListener('message',function(e){
       input.value= e.data
       console.log(e.data);
   })
</script>
</html>
20170317_182426.gif

版權(quán)歸饑人谷 楠柒 所有 如有轉(zhuǎn)載請附上地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市它呀,隨后出現(xiàn)的幾起案子赂摆,更是在濱河造成了極大的恐慌,老刑警劉巖钟些,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绊谭,居然都是意外死亡政恍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門达传,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篙耗,“玉大人,你說我怎么就攤上這事宪赶∽谕洌” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵搂妻,是天一觀的道長蒙保。 經(jīng)常有香客問我,道長欲主,這世上最難降的妖魔是什么邓厕? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮扁瓢,結(jié)果婚禮上详恼,老公的妹妹穿的比我還像新娘。我一直安慰自己引几,他們只是感情好昧互,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般敞掘。 火紅的嫁衣襯著肌膚如雪叽掘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天渐逃,我揣著相機與錄音够掠,去河邊找鬼。 笑死茄菊,一個胖子當(dāng)著我的面吹牛疯潭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播面殖,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼竖哩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脊僚?” 一聲冷哼從身側(cè)響起相叁,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辽幌,沒想到半個月后增淹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡乌企,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年虑润,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片加酵。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡拳喻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猪腕,到底是詐尸還是另有隱情冗澈,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布陋葡,位于F島的核電站亚亲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏腐缤。R本人自食惡果不足惜朵栖,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柴梆。 院中可真熱鬧陨溅,春花似錦、人聲如沸绍在。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至臼寄,卻和暖如春霸奕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吉拳。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工质帅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人留攒。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓煤惩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炼邀。 傳聞我的和親對象是個殘疾皇子魄揉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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