You don't know cross-origin

cross-origin

Why

為什么會(huì)存在跨域問(wèn)題

  • 同源策略
    由于出于安全考慮愚铡,瀏覽器規(guī)定JavaScript不能操作其他域下的頁(yè)面DOM伶授,不能接受其他域下的xhr請(qǐng)求(不只是js,引用非同域下的字體文件,還有canvas引用非同域下的圖片峻呕,也被同源策略所約束)
    只要協(xié)議、域名趣效、端口有一者不同瘦癌,就被視為非同域。

How

如何解決

要解決跨域問(wèn)題英支,就要繞過(guò)瀏覽器對(duì)js的限制佩憾,另辟蹊徑

  1. JSONP
    這是最簡(jiǎn)單,也是最流行的跨域解決方案干花,它利用script標(biāo)簽不受同源策略的影響妄帘,解決跨域,需要后臺(tái)配合池凄,返回特殊格式的數(shù)據(jù)

前端

<script>
    function JSONP(link) {
        let script=document.createElement("script");
        script.src=link;
        document.body.appendChild(script);
    }

    function getUser(data) {
        console.log(data);// todo
    }
    const API_URL_USER='http://cache.video.iqiyi.com/jp/avlist/202861101/1/?callback=getUser'; // 這里以愛奇藝的接口為例(來(lái)源網(wǎng)絡(luò)抡驼,侵刪)
    JSONP(API_URL_USER);
</script>

后端

// Express(Nodejs)
// mock data
const USERS=[
    {name:"Tom",age:23},
    {name:"Jack",age:23}
];

app.get("/user",function (req,res) {
    let cbName=req.query["callback"];
    // 這里做一個(gè)容錯(cuò)處理
    res.send(`
        try{
            ${cbName}(${JSON.stringify(USRES)});
        }catch(ex) {
            console.error("The data is invalid");
        }
    `);
});
  1. CORS (cross-origin resource sharing)
    跨域資源共享,是W3C的一個(gè)標(biāo)準(zhǔn)肿仑,它允許瀏覽器發(fā)送跨域服務(wù)器的請(qǐng)求致盟,CORS需要瀏覽器和服務(wù)器同時(shí)支持

后端

簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求詳情,請(qǐng)閱讀阮一峰老師的博文尤慰,這里不再敖述

app.use(function (req,res,next){
    res.header('Access-Control-Allow-Origin', 'http://localhost:6666'); // 允許跨域的白名單馏锡,一般不建議使用 * 號(hào)
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); // 允許請(qǐng)求的方法,非簡(jiǎn)單請(qǐng)求伟端,會(huì)進(jìn)行預(yù)檢
    res.header('Access-Control-Allow-Headers', 'Content-Type'); // 允許請(qǐng)求攜帶的頭信息杯道,非簡(jiǎn)單請(qǐng)求,會(huì)進(jìn)行預(yù)檢
    res.header('Access-Control-Allow-Credentials','true'); // 允許發(fā)送cookie责蝠,這里前端xhr也需要一起配置 `xhr.withCredentials=true`
    next();
});

  1. 代理
    只要是在與你同域下的服務(wù)器党巾,新建一個(gè)代理(服務(wù)端不存在同源策略),將你的跨域請(qǐng)求全部代理轉(zhuǎn)發(fā)

后端

const proxy=require("http-proxy-middleware"); // 這里使用這個(gè)中間件完成代理
app.use('/api', proxy("http://b.com")); // http://a.com/api -> http://b.com/api
  1. window.name+iframe
    MDN里解釋道它是獲取/設(shè)置窗口的名稱霜医,因?yàn)榈乃诓煌?yè)面甚至域名加載后值都不會(huì)改變齿拂,該屬性也被用于作為 JSONP 的一個(gè)更安全的備選來(lái)提供跨域通信(cross-domain messaging)

前端

<!--http://a.com/page1.html-->
<script>
    function request(url,callback) {
        let iframe=document.createElement("iframe");
        let isFirst=true;
        iframe.style.display="none";
        iframe.addEventListener("load",function () {
            if (isFirst) { 
                isFirst=false; // 防止iframe循環(huán)加載
                iframe.src="http://a.com/page2.html";
                callback && callback(iframe.contentWindow.name);
                iframe.remove();               
            }
        });
        iframe.src=url;
    }

    requeset("http://b.com/user",function (data) {
        console.log(data); // todo
    });
</script>

后端

// Express(Nodejs)
// mock data
const USERS=[
    {name:"Tom",age:23},
    {name:"Jack",age:23}
];

app.get("/user",function (req,res) {
    res.send(`
        <script>
            ;window.name=${JSON.stringify(USERS)};
        </script>
    `);
});
  1. document.domian
    這個(gè)使用情況有限,例如
    http://a.c.com
    http://b.c.com
    主域相同時(shí)肴敛,分別設(shè)置他們頁(yè)面的document.domain="c.com";

  2. locaction.hash+iframe
    嵌套兩層iframe署海,達(dá)到第一層與第三層同域,就可以互相通信了

<!--http://a.com/page1.html-->
<script>
    let iframe=document.createElement("iframe");
    iframe.style.display="none";
    iframe.src="http://b.com/user.html";

    window.addEventListener("hashchange",function () {
        console.log(location.hash.slice(1)); // todo
    });
</script>
<!--http://b.com/user.html-->
<script>
    let iframe=document.createElement("iframe");
    iframe.style.display="none";

    function getUserData() {
        fetch("http://b.com/user")
            .then(res=>{
                let data=res.json();
                iframe.src=`http://a.com/page2.html#${data}`;
            });
    }

    getUserData();

    window.addEventListener("hashchange",function () {
        getUserData();
    });
</script>
<script>
    top.location.hash=window.location.hash;
</script>
  1. 圖片ping
    這個(gè)只能發(fā)出去請(qǐng)求,無(wú)法獲取到服務(wù)器的響應(yīng)叹侄,常常用于網(wǎng)站流量統(tǒng)計(jì)
let img=new Image();
img.addEventListener("load",function () {
    console.log("Send success"); // todo
});
img.src="http://site.c.com/a.gif?count=666";
  1. postMessage+iframe
<!-- http://a.com -->
<button id="sendBtn">從B接口獲取用戶數(shù)據(jù)</button>
<iframe src="http://b.com" id="ifr"></iframe>
<script>
window.addEventListener("message",function({detail,origin}){
    if (origin==="http://b.com") { // 最好判斷下消息來(lái)源
        if (detail.type==="set-user") {
            console.log(detail.data); // todo
        }
    }
});

sendBtn.addEventListener("click",function () {
    ifr.contentWindow.postMessage({
        type:"get-user",
    },"http://b.com");
});
</script>
<!-- http://b.com -->
<script>
window.addEventListener("messagae",function({detail,origin}){
    if (origin==="http://a.com") { // 最好判斷下消息來(lái)源
        if (detail.type==="get-user") {
            fetch("http://b.com/user")
            .then(res=>{
                top.contentWindow.postMessage({
                    type:"set-user",
                    data:res.json(), // 假設(shè)接口返回的是json格式的數(shù)據(jù)
                },"http://a.com");
            })
        }
    }
});


</script>
  1. postMessage+form+iframe

這個(gè)需要后臺(tái)配合返回特殊格式的數(shù)據(jù)巩搏,TL,DR 可以看這個(gè)demo

  1. WebSocket

WebSocket是一種通信協(xié)議,該協(xié)議不實(shí)行同源政策趾代,
注意需要瀏覽器和服務(wù)器都支持的情況下

 <script src="http://cdn.bootcss.com/socket.io/1.7.2/socket.io.min.js"></script>
 <script>
    var io = io.connect('http://b.com');
    io.on('data', function (data) {
        console.log(data); // 接受來(lái)自服務(wù)器的消息
    });
</script>

后端

// Nodejs
const server = require('http').createServer();
const io = require('socket.io')(server);

io.on('connection', function (client) {
    client.emit('data', 'This message from "http://b.com"');
});

Summary

  • 目前個(gè)人在工作中遇到的解決方法就是這些贯底,當(dāng)然還有許多其他的方法,你看撒强,其實(shí)跨域并不難吧 _
  • js通過(guò)xhr發(fā)的跨域請(qǐng)求禽捆,雖然得不到響應(yīng),但是可以發(fā)送出去飘哨,其實(shí)如果是單向通信的話胚想,也可以,比如文章閱讀統(tǒng)計(jì)芽隆,網(wǎng)站流量統(tǒng)計(jì)

Reference

跨域資源共享 CORS 詳解---阮一峰
不要再問(wèn)我跨域的問(wèn)題了
FatDong1/cross-domain

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浊服,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胚吁,更是在濱河造成了極大的恐慌牙躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腕扶,死亡現(xiàn)場(chǎng)離奇詭異孽拷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)半抱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門脓恕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人窿侈,你說(shuō)我怎么就攤上這事炼幔。” “怎么了史简?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵江掩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我乘瓤,道長(zhǎng),這世上最難降的妖魔是什么策泣? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任衙傀,我火速辦了婚禮,結(jié)果婚禮上萨咕,老公的妹妹穿的比我還像新娘统抬。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布聪建。 她就那樣靜靜地躺著钙畔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪金麸。 梳的紋絲不亂的頭發(fā)上擎析,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音挥下,去河邊找鬼揍魂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛棚瘟,可吹牛的內(nèi)容都是我干的现斋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼偎蘸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庄蹋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起迷雪,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤限书,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后振乏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔗包,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年慧邮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了调限。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡误澳,死狀恐怖耻矮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忆谓,我是刑警寧澤裆装,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站倡缠,受9級(jí)特大地震影響哨免,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昙沦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一琢唾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盾饮,春花似錦采桃、人聲如沸懒熙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)工扎。三九已至,卻和暖如春衔蹲,著一層夾襖步出監(jiān)牢的瞬間肢娘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工踪危, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔬浙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓贞远,卻偏偏與公主長(zhǎng)得像畴博,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蓝仲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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

  • 什么是跨域 跨域俱病,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的袱结,是瀏覽器對(duì)JavaScript實(shí)...
    他方l閱讀 1,064評(píng)論 0 2
  • 題目1.什么是同源策略? 同源策略(Same origin Policy): 瀏覽器出于安全方面的考慮亮隙,只允許與本...
    FLYSASA閱讀 1,724評(píng)論 0 6
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本垢夹。它是由瀏覽器的同源策略造成的溢吻,是瀏覽器對(duì)JavaScript實(shí)...
    Yaoxue9閱讀 1,301評(píng)論 0 6
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本果元。它是由瀏覽器的同源策略造成的促王,是瀏覽器對(duì)JavaScript實(shí)...
    HeroXin閱讀 836評(píng)論 0 4
  • 衣服 兩首 一、味道 2018-6-3 你說(shuō)你這衣服好看而晒, 是咸味的吧蝇狼, 我說(shuō)是的,我不喜歡吃甜倡怎。 你說(shuō)你看的出來(lái)...
    記牧閱讀 123評(píng)論 0 0