內(nèi)容主要來源于:ajax跨域完全講解
本文主要講解跨域的產(chǎn)生問題及解決思路盆佣,并不直接給出某個(gè)具體問題的解決方案
1. 跨域的產(chǎn)生
跨域是瀏覽器的限制
凡是發(fā)送請(qǐng)求url的協(xié)議,域名坝疼,端口三者之間任意一個(gè)與當(dāng)前頁面地址不同即是跨域
-
同源策略:
指的是瀏覽器對(duì)不同源的腳本或者文本的訪問方式進(jìn)行的限制。比如源a的js不能讀取或設(shè)置引入的源b的元素屬性谆沃。那么先定義下什么是同源裙士,所謂同源,就是指兩個(gè)頁面具有相同的協(xié)議管毙,主機(jī)(也常說域名),端口桌硫,三個(gè)要素缺一不可夭咬。
受同源策略限制的內(nèi)容
js中的XMLHttpRequest等請(qǐng)求不受同源策略限制的內(nèi)容
頁面中的鏈接:跨域資源嵌入,如<script src="..."></script>铆隘,<img>卓舵,<link>,<iframe>等膀钠,但瀏覽器限制了js不能讀寫加載的內(nèi)容掏湾,
2. html解決跨域
iframe:
這個(gè)功能主要包括接受信息的"message"事件和發(fā)送消息的"postMessage"方法。比如baidu.com域的A頁面通過iframe嵌入了一個(gè)google.com域的B頁面肿嘲,可以通過以下方法實(shí)現(xiàn)A和B的通信
- A頁面通過postMessage方法發(fā)送消息:
window.onload = function() {
var ifr = document.getElementById('ifr');
var targetOrigin = "http://www.google.com";
ifr.contentWindow.postMessage('hello world!', targetOrigin);
};
- B頁面通過message事件監(jiān)聽并接受消息:
var onmessage = function (event) {
var data = event.data;//消息
var origin = event.origin;//消息來源地址
var source = event.source;//源Window對(duì)象
if(origin=="http://www.baidu.com"){
console.log(data);//hello world!
}
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
//for ie
window.attachEvent('onmessage', onmessage);
}
3. 跨域解決思路
3.1 瀏覽器禁止檢查
在控制臺(tái)輸入下面內(nèi)容融击,即可允許跨域
chrome --disable-web-security --user-data-dir=g:\temp3
缺點(diǎn):瀏覽器禁止檢查是客戶端的行為,但是不可能在每個(gè)人的電腦上都通過這個(gè)方法去解決
3.2 jsonp(json pending)
jsonp是動(dòng)態(tài)創(chuàng)建script標(biāo)簽雳窟,使用完后就立即銷毀尊浪,所以無法通過查看dom元素頁面代碼的形式查看jsonp是否生成script腳本。
使用jsonp時(shí),后臺(tái)需要返回javascript腳本(后臺(tái)需要改動(dòng))拇涤,它是一個(gè)函數(shù)捣作,參數(shù)是使用的返回值。
實(shí)際項(xiàng)目中JSONP通常用來獲取json格式數(shù)據(jù)鹅士,這時(shí)前后端通常約定一個(gè)參數(shù)callback券躁,該參數(shù)的值,就是處理返回?cái)?shù)據(jù)的函數(shù)名稱掉盅。
// 前端請(qǐng)求
$.ajax({
method: 'get',
url: config + '/admin/login/getLoginState',
dataType: 'jsonp',
success: (res) => {
if (res.username) {
this.setState({
userName: res.username,
isLogin: true
});
}
}
});
// 后臺(tái)代碼
async getLoginStateAction() {
var role = await this.session('role');
var username = await this.session('username');
var result = {
role: role,
username: username
};
this.jsonp(result);
}
缺點(diǎn):
- 服務(wù)器需要改動(dòng)代碼支持
- 只支持get
- 發(fā)出的不是xhr請(qǐng)求
3.3 跨域:
3.3.1 被調(diào)用方? 支持代理
基于http在支持跨域上的一些規(guī)定也拜,在響應(yīng)頭上添加指定字段,告訴瀏覽器怔接,它允許被調(diào)用方調(diào)用搪泳。
實(shí)現(xiàn)方法:
- 服務(wù)器端實(shí)現(xiàn)
向服務(wù)器端添加以下請(qǐng)求頭
Access-Control-Allow-Origin: * // 允許所有的域名訪問,可以根據(jù)請(qǐng)求的頭來變化
Access-Control-Allow-Methods: * // 允許所有的方法訪問扼脐,可以根據(jù)請(qǐng)求的頭來變化
?? 跨域請(qǐng)求是先執(zhí)行請(qǐng)求岸军,還是先判斷是跨域的請(qǐng)求?
請(qǐng)求有簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求之分瓦侮,如果是簡(jiǎn)單請(qǐng)求艰赞,瀏覽器會(huì)先請(qǐng)求后判斷是否跨域,如果是非簡(jiǎn)單請(qǐng)求肚吏,瀏覽器會(huì)發(fā)送域解命令通過之后在請(qǐng)求方妖。
?? 注:
- 簡(jiǎn)單請(qǐng)求:
GET、HEAD罚攀、POST
請(qǐng)求頭中党觅,無自定義頭
Content-Type為以下幾種:text/plain、multipart/form-data斋泄、application/x-www-form-urlaencoded- 非簡(jiǎn)單請(qǐng)求:
PUT杯瞻、delete方法的Ajax請(qǐng)求
發(fā)送json格式的Ajax請(qǐng)求
帶自定義頭的Ajax請(qǐng)求
?? Access-Control-Allow-Origin: * // 允許所有的域名訪問,可以根據(jù)請(qǐng)求的頭來變化
是否滿足所有請(qǐng)求呢炫掐?
使用帶cookie的請(qǐng)求時(shí)魁莉,會(huì)出現(xiàn)以下問題:
這是因?yàn)樵谑褂脦ookie的請(qǐng)求時(shí),必須將
Access-Control-Allow-Origin
設(shè)置為對(duì)應(yīng)的域名募胃,而不是允許所有的域名
將指定為對(duì)應(yīng)的域名后旗唁,會(huì)出現(xiàn):
將
Access-Control-Allow-Credentials
設(shè)置為true
,讓服務(wù)器允許cookie
即可
但是這樣請(qǐng)求會(huì)讓服務(wù)端局限與一個(gè)域名調(diào)用
解決思路:
將客戶端的請(qǐng)求origin
獲取到痹束,把它設(shè)置為Access-Control-Allow-Origin
就可以允許所有的域名調(diào)用
將客戶端的請(qǐng)求header
獲取到检疫,把它設(shè)置為Access-Control-Allow-Headers
就可以允許所有的自定義請(qǐng)求頭
-
nginx配置
-
apache配置
3.3.2 調(diào)用方? 隱藏代理
跨域請(qǐng)求不是從瀏覽器直接發(fā)送到被調(diào)用方,而是從中間的服務(wù)器轉(zhuǎn)發(fā)的
反向代理:
nginx配置
apache配置