前端跨域整理

什么是跨域?

跨域是指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源,這里的跨域是廣義的溅话。

廣義的跨域:

  1. 資源跳轉(zhuǎn):a鏈接晓锻、重定向、表單提交飞几;
  2. 資源嵌入:<link>砚哆、<script>、<img>屑墨、<frame>等DOM標(biāo)簽
  3. 腳本請(qǐng)求:ajax躁锁、dom對(duì)象跨域操作等。

狹義的跨域:
其實(shí)我們通常所說的跨域是狹義的卵史,是由瀏覽器同源策略限制的一類請(qǐng)求場(chǎng)景战转。

同源策略/SOP(Same origin policy)是一種約定,由Netscape公司1995年引入瀏覽器以躯,它是瀏覽器最核心也最基本的安全功能槐秧,如果缺少了同源策略,瀏覽器很容易受到XSS寸潦、CSFR等攻擊。所謂同源是指"協(xié)議+域名+端口"三者相同社痛,即便兩個(gè)不同的域名指向同一個(gè)ip地址见转,也非同源。

同源策略限制一下幾種行為:

  1. Cookie蒜哀、LocalStorage和IndexDB無法獲取
  2. DOM和JS對(duì)象無法獲得
  3. Ajax請(qǐng)求不能發(fā)送

常見跨域場(chǎng)景:

場(chǎng)景                     | example                      | 是否允許通信

同一域名斩箫,不同文件或路徑     | http://www.a.com/a           | 允許
                         | http://www.a.com/b           |

同一域名,不同端口          | http://www.a.com:8082/a      | 不允許
                         | http://www.a.com/a           |

同一域名撵儿,不同協(xié)議          | http://www.a.com/a           | 不允許
                         | https://www.a.com/a          |

同一IP,不同域名            | http://www.a.com/a           | 不允許
                         | http://192.168.1.1/a         |

主域名相同乘客,子域名不同       | http://www.a.com/a           | 不允許
                         | http://domain.a.com/a        |

不同域名,                 | http://www.a.com/a           | 不允許
                         | http://www.b.com/a           |

跨域的解決方案

  1. 通過jsonp跨域淀歇;

    通常為了減輕web服務(wù)器的負(fù)載易核,我們把js、css浪默,img等靜態(tài)資源分離到另一臺(tái)獨(dú)立域名的服務(wù)器上牡直,在html頁面中再通過相應(yīng)的標(biāo)簽從不同域名下加載靜態(tài)資源,而被瀏覽器允許纳决,基于此原理碰逸,我們可以通過動(dòng)態(tài)創(chuàng)建script,再請(qǐng)求一個(gè)帶參網(wǎng)址實(shí)現(xiàn)跨域通信阔加。

    原理:

    // 前端
    <script>
        var script = document.createElement('script');
        script.type = 'text/javascript';
    
        // 傳參并指定回調(diào)執(zhí)行函數(shù)為onBack
        script.src = 'http://www.a.com/login?user=admin&callback=onBack';
        document.head.appendChild(script);
    
        // 回調(diào)執(zhí)行函數(shù)
        function onBack(res) {
            alert(JSON.stringify(res));
        }
     </script>
     
     // 服務(wù)器返回如下(返回時(shí)即執(zhí)行全局函數(shù))
     onBack({"status": true, "user": "admin"})
    

    JSONP缺點(diǎn):只能實(shí)現(xiàn)get一種請(qǐng)求饵史。

  2. 跨域資源共享(CORS);

    普通跨域請(qǐng)求:只服務(wù)端設(shè)置Access-Control-Allow-Origin即可,前端無須設(shè)置胳喷,若要帶cookie請(qǐng)求:前后端都需要設(shè)置湃番。(需注意的是:由于同源策略的限制,所讀取的cookie為跨域請(qǐng)求接口所在域的cookie厌蔽,而非當(dāng)前頁牵辣。

    Java后臺(tái):

    /*
     * 導(dǎo)入包:import javax.servlet.http.HttpServletResponse;
     * 接口參數(shù)中定義:HttpServletResponse response
     */
    
    // 允許跨域訪問的域名:若有端口需寫全(協(xié)議+域名+端口),若沒有端口末尾不用加'/'
    response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 
    
    // 允許前端帶認(rèn)證cookie:?jiǎn)⒂么隧?xiàng)后奴饮,上面的域名不能為'*'纬向,必須指定具體的域名,否則瀏覽器會(huì)提示
    response.setHeader("Access-Control-Allow-Credentials", "true"); 
    
    // 提示OPTIONS預(yù)檢時(shí)戴卜,后端需要設(shè)置的兩個(gè)常用自定義頭
    response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
    

    nodejs后臺(tái):

    var http = require('http');
    var server = http.createServer();
    var qs = require('querystring');
    
    server.on('request', function(req, res) {
        var postData = '';
    
        // 數(shù)據(jù)塊接收中
        req.addListener('data', function(chunk) {
            postData += chunk;
        });
    
        // 數(shù)據(jù)接收完畢
        req.addListener('end', function() {
            postData = qs.parse(postData);
    
            // 跨域后臺(tái)設(shè)置
            res.writeHead(200, {
                'Access-Control-Allow-Credentials': 'true',     // 后端允許發(fā)送Cookie
                'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允許訪問的域(協(xié)議+域名+端口)
                /* 
                 * 此處設(shè)置的cookie還是domain2的而非domain1逾条,因?yàn)楹蠖艘膊荒芸缬驅(qū)慶ookie(nginx反向代理可以實(shí)現(xiàn)),
                 * 但只要domain2中寫入一次cookie認(rèn)證投剥,后面的跨域接口都能從domain2中獲取cookie师脂,從而實(shí)現(xiàn)所有的接口都能跨域訪問
                 */
                'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是讓js無法讀取cookie
            });
    
            res.write(JSON.stringify(postData));
            res.end();
        });
    });
    
    server.listen('8080');
    console.log('Server is running at port 8080...');
    

    CORS也已經(jīng)成為主流的跨域解決方案。

  3. nginx代理跨域江锨;

    nginx配置解決iconfont跨域吃警。

    瀏覽器跨域訪問js、css啄育、img等常規(guī)靜態(tài)資源被同源策略許可酌心,但iconfont字體文件(eot|otf|ttf|woff|svg)例外,此時(shí)可在nginx的靜態(tài)資源服務(wù)器中加入以下配置挑豌。

    location / {
       add_header Access-Control-Allow-Origin *;
    }
    

    nginx反向代理接口跨域

    跨域原理: 同源策略是瀏覽器的安全策略安券,不是HTTP協(xié)議的一部分。服務(wù)器端調(diào)用HTTP接口只是使用HTTP協(xié)議氓英,不會(huì)執(zhí)行JS腳本侯勉,不需要同源策略,也就不存在跨越問題铝阐。

    實(shí)現(xiàn)思路:通過nginx配置一個(gè)代理服務(wù)器(域名與domain1相同址貌,端口不同)做跳板機(jī),反向代理訪問domain2接口徘键,并且可以順便修改cookie中domain信息芳誓,方便當(dāng)前域cookie寫入,實(shí)現(xiàn)跨域登錄啊鸭。

    nginx具體配置:

    #proxy服務(wù)器
    server {
        listen       81;
        server_name  www.domain1.com;
    
        location / {
            proxy_pass   http://www.domain2.com:8080;  #反向代理
            proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
            index  index.html index.htm;
    
            # 當(dāng)用webpack-dev-server等中間件代理接口訪問nignx時(shí)锹淌,此時(shí)無瀏覽器參與,故沒有同源限制赠制,下面的跨域配置可不啟用
            add_header Access-Control-Allow-Origin http://www.domain1.com;  #當(dāng)前端只跨域不帶cookie時(shí)赂摆,可為*
            add_header Access-Control-Allow-Credentials true;
        }
    }
    
  4. nodejs中間件代理跨域挟憔;

    node中間件實(shí)現(xiàn)跨域代理,原理大致與nginx相同烟号,都是通過啟一個(gè)代理服務(wù)器绊谭,實(shí)現(xiàn)數(shù)據(jù)的轉(zhuǎn)發(fā),也可以通過設(shè)置cookieDomainRewrite參數(shù)修改響應(yīng)頭中cookie中域名汪拥,實(shí)現(xiàn)當(dāng)前域的cookie寫入达传,方便接口登錄認(rèn)證。

  5. webSocket協(xié)議跨域迫筑;

    WebSocket protocol是HTML5一種新的協(xié)議宪赶。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信,同時(shí)允許跨域通訊脯燃,是server push技術(shù)的一種很好的實(shí)現(xiàn)搂妻。

  6. postMessage跨域;

    postMessage是HTML5 XMLHttpRequest Level 2中的API辕棚,且是為數(shù)不多可以跨域操作的window屬性之一欲主,它可用于解決以下方面的問題

    • 頁面和其打開的新窗口的數(shù)據(jù)傳遞;
    • 多窗口之間消息傳遞逝嚎;
    • 頁面與嵌套的iframe消息傳遞扁瓢;

    用法:postMessage(data,origin)方法接受兩個(gè)參數(shù)

    data: html5規(guī)范支持任意基本類型或可復(fù)制的對(duì)象,但部分瀏覽器只支持字符串补君,所以傳參時(shí)最好用JSON.stringify()序列化引几。

    origin: 協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為"*"赚哗,表示可以傳遞給任意窗口硅堆,如果要指定和當(dāng)前窗口同源的話設(shè)置為"/"屿储。

    1. a.html
    <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
    <script>       
        var iframe = document.getElementById('iframe');
        iframe.onload = function() {
            var data = {
                name: 'aym'
            };
            // 向domain2傳送跨域數(shù)據(jù)
            iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
        };
    
        // 接受domain2返回?cái)?shù)據(jù)
        window.addEventListener('message', function(e) {
            alert('data from domain2 ---> ' + e.data);
        }, false);
    </script>
    
    1. b.html
    <script>
        // 接收domain1的數(shù)據(jù)
        window.addEventListener('message', function(e) {
            alert('data from domain1 ---> ' + e.data);
    
            var data = JSON.parse(e.data);
            if (data) {
                data.number = 16;
    
                // 處理后再發(fā)回domain1
                window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
            }
        }, false);
    </script>
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渐逃,一起剝皮案震驚了整個(gè)濱河市够掠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茄菊,老刑警劉巖疯潭,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異面殖,居然都是意外死亡竖哩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門脊僚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來相叁,“玉大人,你說我怎么就攤上這事≡鲅停” “怎么了椿访?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)虑润。 經(jīng)常有香客問我成玫,道長(zhǎng),這世上最難降的妖魔是什么拳喻? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任哭当,我火速辦了婚禮,結(jié)果婚禮上舞蔽,老公的妹妹穿的比我還像新娘荣病。我一直安慰自己,他們只是感情好渗柿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布个盆。 她就那樣靜靜地躺著,像睡著了一般朵栖。 火紅的嫁衣襯著肌膚如雪颊亮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天陨溅,我揣著相機(jī)與錄音终惑,去河邊找鬼。 笑死门扇,一個(gè)胖子當(dāng)著我的面吹牛雹有,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臼寄,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼霸奕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了吉拳?” 一聲冷哼從身側(cè)響起质帅,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎留攒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炼邀,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡洛退,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了不狮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驻仅,靈堂內(nèi)的尸體忽然破棺而出毡泻,到底是詐尸還是另有隱情粘优,我是刑警寧澤雹顺,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布贩挣,位于F島的核電站王财,受9級(jí)特大地震影響绒净,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疯溺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一哎垦、第九天 我趴在偏房一處隱蔽的房頂上張望漏设。 院中可真熱鬧今妄,春花似錦鸳碧、人聲如沸犬性。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹤耍。三九已至,卻和暖如春稿黄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背族购。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工联四, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撑教,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓收苏,卻偏偏與公主長(zhǎng)得像愤兵,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子懦鼠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • 原文地址:原文地址 什么是跨域肛冶? 跨域是指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源扯键,這里跨域是廣義的。 廣義...
    C_Y大漁閱讀 1,259評(píng)論 1 13
  • 前言 原文地址:前端跨域總結(jié) 博主博客地址:Damonare的個(gè)人博客 相信每一個(gè)前端er對(duì)于跨域這兩個(gè)字都不會(huì)陌...
    秦至閱讀 1,398評(píng)論 4 51
  • 前言 原文地址:前端跨域總結(jié)博主博客地址:Damonare的個(gè)人博客 正文 1. 什么是跨域伦乔? 跨域一詞從字面意思...
    yo_yo_閱讀 500評(píng)論 0 5
  • 什么是跨域 跨域董习,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的皿淋,是瀏覽器對(duì)JavaScript實(shí)...
    HeroXin閱讀 836評(píng)論 0 4
  • 賬戶所用 SSH 秘鑰生成 將生成的 SSH 秘鑰登錄到 Github 賬號(hào)中 登錄 Github 賬戶材鹦,進(jìn)入 h...
    AchillesSatan閱讀 1,152評(píng)論 2 4