前端跨域問題(轉(zhuǎn)載筆記)

前端跨域問題

瀏覽器的同源策略

提到跨域不能不先說一下”同源策略”悯姊。
何為同源渊涝?只有當(dāng)協(xié)議渣慕、端口贵试、和域名都相同的頁面,則兩個(gè)頁面具有相同的源当辐。只要網(wǎng)站的 協(xié)議名protocol、 主機(jī)host、 端口號(hào)port 這三個(gè)中的任意一個(gè)不同哪廓,網(wǎng)站間的數(shù)據(jù)請(qǐng)求與傳輸便構(gòu)成了跨域調(diào)用,會(huì)受到同源策略的限制初烘。
同源策略限制從一個(gè)源加載的文檔或腳本如何與來自另一個(gè)源的資源進(jìn)行交互涡真。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制。瀏覽器的同源策略肾筐,出于防范跨站腳本的攻擊哆料,禁止客戶端腳本(如 JavaScript)對(duì)不同域的服務(wù)進(jìn)行跨站調(diào)用(通常指使用XMLHttpRequest請(qǐng)求)。

跨域請(qǐng)求方式

解決跨域問題吗铐,最簡(jiǎn)單的莫過于通過nginx反向代理進(jìn)行實(shí)現(xiàn)东亦,但是其需要在運(yùn)維層面修改,且有可能請(qǐng)求的資源并不再我們控制范圍內(nèi)(第三方)唬渗,所以該方式不能作為通用的解決方案典阵,下面闡述了經(jīng)常用到幾種跨域方式:

首先奋渔,先設(shè)置一下環(huán)境來調(diào)試跨域問題

1、利用node.js作為服務(wù)端壮啊,關(guān)于搭建就用 node express 快速搭建

<figure style="margin: 1em 0px;">
image

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">生成一個(gè)測(cè)試應(yīng)用demo</figcaption>

</figure>

2嫉鲸、本地配置域名

打開hosts,授予管理權(quán)限并打開編輯歹啼,加入

127.0.0.1 www.mynodetest.com

127.0.0.1 child.mynodetest.com

打開終端玄渗,cd進(jìn)入myapp目錄,執(zhí)行

node start

在publish文件中創(chuàng)建index.html文件

<figure style="margin: 1em 0px;">
image

</figure>

然后打開瀏覽器染突,執(zhí)行 www.mytestnode.com/index.html 就能訪問 index.html頁面

如果網(wǎng)頁提示域名解析錯(cuò)誤捻爷,那么就是本地網(wǎng)絡(luò)設(shè)置了固定ip,dns份企,代理的問題
① 將internet協(xié)議版本如(TCP/IPv4)中的固定值設(shè)置為自動(dòng)獲取也榄,同時(shí)取消代理

<figure style="margin: 1em 0px;">
image

</figure>

<figure style="margin: 1.6em 0px 1em;">
image

</figure>

② 代理中直接加入過濾 mynodetest.com

<figure style="margin: 1em 0px;">
image

</figure>

基本調(diào)試環(huán)境設(shè)置完畢,開始嘗試跨域方式

一司志、jsonp (只能支持get請(qǐng)求)

打開 myapp/routes/index.js甜紫,加入一個(gè)jsonp請(qǐng)求

<figure style="margin: 1em 0px;">
image

</figure>

同時(shí)在html也加入一個(gè)script

<figure style="margin: 1em 0px;">
image

</figure>

刷新瀏覽器,地址還是 www.mynodetest.com/index.html

<figure style="margin: 1em 0px;">
image

</figure>

二骂远、document.domain + iframe

僅限主域相同囚霸,子域不同的應(yīng)用場(chǎng)景
瀏覽器訪問的地址是 http://www.mynodetest.com:3000/index.html
嵌入iframe的地址是 http://child.mynodetest.com:3000/test.html
都用js手動(dòng)設(shè)置 document.domain = 'mynodetest.com'; 作為基礎(chǔ)域,繼而實(shí)現(xiàn)同域

myapp/publish 新增test.html激才, 并加入以下代碼

<figure style="margin: 1em 0px;">
image

</figure>

index.html中加入 user拓型, 并使用iframe嵌入 http://child.mynodetest.com:3000/test.html

<figure style="margin: 1em 0px;">
image

</figure>

好了,此時(shí)重新刷新頁面瘸恼,在控制臺(tái)能看見跨域的問題了劣挫,

<figure style="margin: 1em 0px;">
image

</figure>

把test.html中注釋的地方取消注釋,在刷新頁面

<figure style="margin: 1em 0px;">
image

</figure>

就能看到在index.html中定義的 user

三东帅、location.has + iframe

a 與 b 不同域压固,但是相互之間要進(jìn)行通信,可以通過中間頁 c 與(a 同域)實(shí)現(xiàn)靠闭,不同域使用hash傳值帐我,相同域使用js訪問

1、 a.html (http://www.mynodetest.com:3000/a.html)

<body>
    <iframe id='iframe' src="http://child.mynodetest.com:3000/b.html" frameborder="0"></iframe>
    <button id='button'>set b.html #user</button>
</body>

<script>
    document.getElementById('button').onclick = () => {
        var iframe = document.getElementById('iframe');
        iframe.src = `${iframe.src}#user=mynodetest`;
    }
    function cb(result) {
        console.log(result);
    }
</script>

2愧膀、b.html (http://child.mynodetest.com:3000/b.html)

<body>
    <iframe id='iframe' src="http://www.mynodetest.com:3000/c.html" frameborder="0"></iframe>
</body>

<script>
    var iframe = document.getElementById('iframe');
    window.onhashchange = () => {
        iframe.src = `${iframe.src}${location.hash}`;
    }
</script>

3拦键、c.html (http://www.mynodetest.com:3000/c.html)

<script>
    window.onhashchange = () => {
        window.parent.parent.cb(location.hash.replace('#user=', ''));
    }
</script>

四、window.name + iframe

window.name 在不用的地址(甚至不同域名)加載后依舊存在于該窗口扇调,上限 2M
通過 iframe.src 外域轉(zhuǎn)向本地域矿咕,跨域數(shù)據(jù)由 iframe 的 window.name 從外域傳到本地域,繞過瀏覽器的訪問限制,同時(shí)又是安全操作

1碳柱、a.html (http://www.mynodetest.com:3000/a.html)

const proxy = (url, cb) => {
    let state = 0;
    let iframe = document.createElement('iframe');
    iframe.src = url;
    iframe.onload = () => {
        if(state === 0) {
            iframe.contentWindow.location = 'http://www.mynodetest.com:3000/c.html';
            state = 1;
        } else if(state === 1) {
            cb(iframe.contentWindow.name);
            iframe.contentWindow.document.write('');
            iframe.contentWindow.close();
            document.body.removeChild(iframe);
        }
    }
    document.body.appendChild(iframe);
}
proxy('http://child.mynodetest.com:3000/b.html', name => console.log(name));

2捡絮、c.html (http://www.mynodetest.com:3000/c.html)

沒什么內(nèi)容,主要的作用是與 a 作用域相同

3莲镣、b.html (http://child.mynodetest.com:3000/b.html)

window.name = 'b.html'

五福稳、postMessage

postMessage是 HTML5 XMLHttpRequest Level 2中的API, 支持瀏覽器跨域操作
用法 postMessage( data, origin )
data:html5 規(guī)范支持任意基本類型或可復(fù)制對(duì)象瑞侮,但部分瀏覽器只支持字符串的圆,可用 JSON.stringify() 序列化。
origin:協(xié)議 + 主機(jī) + 端口號(hào)半火,可以設(shè)置為 "*"越妈,表示可以傳遞給任意窗口,要指定和當(dāng)前窗口同源設(shè)置為 "/"钮糖。

  1. 頁面和其它打開的新窗口的數(shù)據(jù)傳遞
  2. 多窗口之間的信息傳遞
  3. 頁面與嵌套iframe消息傳遞
  4. 以上三個(gè)場(chǎng)景的跨域數(shù)據(jù)傳遞

⑴ a.html (http://www.mynodetest.com:3000/a.html)

<iframe id="iframe" src="http://child.mynodetest.com:3000/b.html"></iframe>

let iframe = document.getElementById('iframe');
iframe.onload = () => {
    const data = { name: 'a to b' };
    iframe.contentWindow.postMessage(JSON.stringify(data), 'http://child.mynodetest.com');
}

window.addEventListener('message', e => {
    console.log(e.data);
})

⑵ b.html (http://child.mynodetest.com:3000/b.html)

window.addEventListener('message', e => {
    console.log(e.data);
    window.parent.postMessage('b to a ', 'http://www.mynodetest.com');
})

六梅掠、跨域資源共享 (CORS)

普通跨域請(qǐng)求: 只需要服務(wù)端設(shè)置 Access-Control-Allow-Origin
帶cookie請(qǐng)求:前后端都需要設(shè)置值,所帶的cookie為跨域請(qǐng)求接口所在域的cookie

1店归、前端請(qǐng)求

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open('post', 'http://child.mynodetest.com:3000/post', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
    if(xhr.readyState == 4 && xhr.status == 200) {
        console.log(xhr.responseText);
    }
}

2阎抒、node (express)

router.post('/post', function(req, res) {
  //req.headers.origin
  res.header("Access-Control-Allow-Origin", 'http://www.mynodetest.com:3000'); //需要顯示設(shè)置來源
  //res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  //res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
  res.header("Access-Control-Allow-Credentials",true); //帶cookies
  //res.header("X-Powered-By",' 3.2.1')
  //res.header("Content-Type", "application/json;charset=utf-8");
  res.header('Set-Cookie', 'l=a123456;Path=/;Domain=child.mynodetest.com;HttpOnly');
  res.json({ test: '22222' });
});

七、nginx代理跨域

1消痛、nginx配置解決iconfont跨域

瀏覽器跨域訪問js且叁、css、img等常規(guī)靜態(tài)資源被同源策略許可秩伞,但iconfont字體文件(eot|otf|ttf|woff|svg)例外逞带,可在nginx的靜態(tài)資源服務(wù)器中加入以下配置。

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

2纱新、nginx反向代理接口跨域

跨域原理:同源策略是瀏覽器的安全策略掰担,不是http協(xié)議的一部分。服務(wù)端調(diào)用http接口只是http協(xié)議怒炸,不會(huì)執(zhí)行js腳本,不需要同源策略毡代,也不存在跨域問題阅羹。

實(shí)現(xiàn)思路:通過nginx配置一個(gè)代理服務(wù)器(域名與domain1相同,端口不同)教寂,反向代理訪問domain2接口捏鱼,可以順便修改cookie中domain信息,方便當(dāng)前域cookie寫入酪耕,實(shí)現(xiàn)跨域
假設(shè)當(dāng)前有兩個(gè)域名 www.domain1.com www.domain2.com

#proxy服務(wù)器
server {
    listen       81;
    server_name  www.my;

    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;
    }
}

⑴ 前端

var xhr = new XMLHttpRequest();

// 前端開關(guān):瀏覽器是否讀寫cookie
xhr.withCredentials = true;

// 訪問nginx中的代理服務(wù)器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

⑵ node

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));

    // 向前臺(tái)寫cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:腳本無法讀取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

八递鹉、node中間件代理

與nginx相同,通過啟動(dòng)一個(gè)代理服務(wù)器藏斩,實(shí)時(shí)數(shù)據(jù)轉(zhuǎn)發(fā)

1躏结、前端

var xhr = new XMLHttpRequest();

// 前端開關(guān):瀏覽器是否讀寫cookie
xhr.withCredentials = true;

// 訪問http-proxy-middleware代理服務(wù)器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();

2、node中間件

var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
    // 代理跨域目標(biāo)接口
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,

    // 修改響應(yīng)頭信息狰域,實(shí)現(xiàn)跨域并允許帶cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    // 修改響應(yīng)信息中的cookie域名
    cookieDomainRewrite: 'www.domain1.com'  // 可以為false媳拴,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');

九、websocket協(xié)議

WebSocket protocol是HTML5一種新的協(xié)議兆览。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信屈溉,同時(shí)允許跨域通訊,是server push技術(shù)的一種很好的實(shí)現(xiàn)抬探。原生WebSocket API使用起來不太方便子巾,我們使用http://Socket.io,它很好地封裝了webSocket接口驶睦,提供了更簡(jiǎn)單砰左、靈活的接口,也對(duì)不支持webSocket的瀏覽器提供了向下兼容场航。

1缠导、前端

<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');

// 連接成功處理
socket.on('connect', function() {
    // 監(jiān)聽服務(wù)端消息
    socket.on('message', function(msg) {
        console.log('data from server: ---> ' + msg); 
    });

    // 監(jiān)聽服務(wù)端關(guān)閉
    socket.on('disconnect', function() { 
        console.log('Server socket has closed.'); 
    });
});

document.getElementsByTagName('input')[0].onblur = function() {
    socket.send(this.value);
};
</script>

2、node socket

var http = require('http');
var socket = require('socket.io');

// 啟http服務(wù)
var server = http.createServer(function(req, res) {
    res.writeHead(200, {
        'Content-type': 'text/html'
    });
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

// 監(jiān)聽socket連接
socket.listen(server).on('connection', function(client) {
    // 接收信息
    client.on('message', function(msg) {
        client.send('hello:' + msg);
        console.log('data from client: ---> ' + msg);
    });

    // 斷開處理
    client.on('disconnect', function() {
        console.log('Client socket has closed.'); 
    });
});
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溉痢,一起剝皮案震驚了整個(gè)濱河市僻造,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孩饼,老刑警劉巖髓削,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異镀娶,居然都是意外死亡立膛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門梯码,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宝泵,“玉大人,你說我怎么就攤上這事轩娶《蹋” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵鳄抒,是天一觀的道長闯捎。 經(jīng)常有香客問我椰弊,道長,這世上最難降的妖魔是什么瓤鼻? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任秉版,我火速辦了婚禮,結(jié)果婚禮上娱仔,老公的妹妹穿的比我還像新娘沐飘。我一直安慰自己,他們只是感情好牲迫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布耐朴。 她就那樣靜靜地躺著,像睡著了一般盹憎。 火紅的嫁衣襯著肌膚如雪筛峭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天陪每,我揣著相機(jī)與錄音影晓,去河邊找鬼。 笑死檩禾,一個(gè)胖子當(dāng)著我的面吹牛挂签,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盼产,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼饵婆,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了戏售?” 一聲冷哼從身側(cè)響起侨核,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灌灾,沒想到半個(gè)月后搓译,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锋喜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年些己,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘿般。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轴总,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出博个,到底是詐尸還是另有隱情,我是刑警寧澤功偿,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布盆佣,位于F島的核電站往堡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏共耍。R本人自食惡果不足惜虑灰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痹兜。 院中可真熱鬧穆咐,春花似錦、人聲如沸字旭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遗淳。三九已至拍柒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屈暗,已是汗流浹背拆讯。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留养叛,地道東北人种呐。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像弃甥,于是被迫代替她去往敵國和親爽室。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • 原文地址:原文地址 什么是跨域潘飘? 跨域是指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源肮之,這里跨域是廣義的。 廣義...
    C_Y大漁閱讀 1,259評(píng)論 1 13
  • 什么是跨域卜录? 2.) 資源嵌入:戈擒、、艰毒、等dom標(biāo)簽筐高,還有樣式中background:url()、@font-fac...
    電影里的夢(mèng)i閱讀 2,374評(píng)論 0 5
  • 題目1.什么是同源策略? 同源策略(Same origin Policy): 瀏覽器出于安全方面的考慮丑瞧,只允許與本...
    FLYSASA閱讀 1,724評(píng)論 0 6
  • 臀腿體能訓(xùn)練柑土,加三十分鐘跑步。 每組間休息時(shí)間10秒绊汹,腿抖稽屏,一地雞毛。 1. 負(fù)重12公斤“鐐銬”深蹲西乖,40 re...
    MissHungry閱讀 439評(píng)論 0 1
  • 中秋夜語 長街孤影直狐榔,落寞煙花謝坛增。 風(fēng)寒不自立,落寞孤燈隨薄腻。 奈何月圓夜收捣,長嘯自伶仃。 落花人獨(dú)立庵楷,微雨燕雙飛罢艾。 ...
    迷離的霧閱讀 345評(píng)論 0 1