1 同源策略
1.1 同源
- window.origin或者location.origin可以得到當(dāng)前源
- 源 = 協(xié)議 + 域名 + 端口號(hào)
- 如果有兩個(gè)url的 協(xié)議构订、域名丛塌、端口號(hào)完全一致空凸,那么這兩個(gè)url就是同源的
- 在瀏覽器里打開頁(yè)面則默認(rèn)遵循同源策略
- 前端測(cè)試時(shí)可以使用 postman 等工具嚎花,或安裝在chrome里安裝插件進(jìn)行測(cè)試(不遵循同源策略,僅測(cè)試開發(fā)時(shí)可以使用)呀洲。
1.2 同源策略定義
- 如果JS運(yùn)行在源A里紊选,那么就只能獲取源A的數(shù)據(jù)
- 不能獲取源B的數(shù)據(jù),即不允許跨域
- 不同源的頁(yè)面之間道逗,不準(zhǔn)互相訪問數(shù)據(jù)
1.3 優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
保證用戶的隱私安全和數(shù)據(jù)安全
缺點(diǎn)
當(dāng)前端需要訪問另一個(gè)域名的后端接口時(shí)兵罢,會(huì)被瀏覽器阻止其獲取相應(yīng)。
eg:A 站點(diǎn)通過(guò) AJAX 訪問 B 站點(diǎn)的 /money 查詢余額接口滓窍,請(qǐng)求可以發(fā)出卖词,但響應(yīng)會(huì)被瀏覽器屏蔽
1.4 疑問解答
- a.qq.com和qq.com跨域:歷史上出現(xiàn)過(guò)不同公司共用域名,因此這兩者不一定是同一個(gè)網(wǎng)站吏夯,瀏覽器謹(jǐn)慎起見此蜈,認(rèn)為這是不同的源
- 不同端口算跨域:一個(gè)端口一個(gè)公司,因此不同端口可能屬于不同公司噪生,因此認(rèn)為是不同的源
- 同IP跨域:原因同上裆赵,IP是可以共用的
- 跨域可以使用CSS、JS和圖片等:同源策略限制的是數(shù)據(jù)訪問跺嗽,我們引用CSS战授、JS和圖片時(shí),其實(shí)并不知道其內(nèi)容桨嫁,只是在引用
1.5 跨域的解決方法
跨域問題根源:
- 瀏覽器默認(rèn)不同源之間不能互相訪問數(shù)據(jù)
解決方法:
- 方法1:CORS(不支持IE)
- 方法2:JSONP(script標(biāo)簽存在缺陷)
2 CORS
- 在server.js中進(jìn)行設(shè)置陈醒,在需要跨域訪問的文件設(shè)置中添加以下代碼:
// 'http://xxxx:yyyy為設(shè)置允許訪問這部分資源的網(wǎng)站地址及端口號(hào)
response.setHeader('Access-Control-Allow-Origin','http://xxxx:yyyy')
- referer
可以通過(guò)console.log(request.headers['referer'])
讀取想要獲取此資源的網(wǎng)站
存在問題
無(wú)法兼容IE6、7瞧甩、8、9瀏覽器弥鹦,因此如果需要兼容IE瀏覽器肚逸,則需要使用JSONP
3 JSONP
3.1 簡(jiǎn)介
3.1.1 概念
在進(jìn)行跨域操作時(shí),由于部分瀏覽器不支持CORS彬坏,因此我們必須使用JSONP來(lái)執(zhí)行跨域操作朦促,我們需要請(qǐng)求一個(gè)JS文件,JS文件中會(huì)執(zhí)行一個(gè)回調(diào)栓始,回調(diào)中包含我們所需的數(shù)务冕,回調(diào)的名稱是隨機(jī)生成的隨機(jī)數(shù),我們將這個(gè)隨機(jī)數(shù)名稱以callback的參數(shù)傳給后臺(tái)幻赚,后臺(tái)會(huì)將函數(shù)返回給我們并執(zhí)行禀忆。
3.1.2 優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 兼容IE
- 可以跨域
缺點(diǎn)
- 由于它是script標(biāo)簽臊旭,所以它無(wú)法讀取到AJAX那么精確,接收不到狀態(tài)碼及響應(yīng)頭等
- 由于它是script標(biāo)簽箩退,因此它只能發(fā)get請(qǐng)求离熏,不支持post
3.2 原理
通過(guò)依靠域內(nèi)js獲取域內(nèi)json的內(nèi)容,而后使用域外js調(diào)用域內(nèi)獲取到內(nèi)容的js戴涝,從而獲得域內(nèi)json的內(nèi)容
3.3 使用方法(以獲取json文件內(nèi)容為例)
- 在json所在域內(nèi)設(shè)置同域js文件用于獲取json文件內(nèi)容
- js文件內(nèi)使用占位符預(yù)留json內(nèi)容放置空間
- 在域內(nèi)server.js文件中設(shè)置滋戳,使用域內(nèi)js文件獲取域內(nèi)對(duì)應(yīng)json文件內(nèi)容
- 域外則可以通過(guò)引用域內(nèi)js文件,獲取域內(nèi)json文件內(nèi)容
3.4 實(shí)例
要求:
1.sherry.com需要獲取qq.com域內(nèi)friend.json的數(shù)據(jù)
2.為回調(diào)函數(shù)設(shè)置隨機(jī)函數(shù)名啥刻,以避免函數(shù)名被占用
3.通過(guò)referer設(shè)置僅限http://sherry.com:9999
可以訪問friend.json的內(nèi)容
// qq.com內(nèi)friend.json的內(nèi)容
{
"name": "sherry",
"age": 18
}
// 在sherry.js定義隨機(jī)函數(shù)奸鸯,并打印數(shù)據(jù),sherry.com可以通過(guò)window[random]獲取數(shù)據(jù)
// window[random]是回調(diào)函數(shù)
const random = Math.random()
window[random] = (data) => {
console.log(data)
}
// qq.com內(nèi)server.js設(shè)置
else if (path === '/friend.js') {
// referer判定是否為http://sherry.com:9999可帽,是進(jìn)行操作娄涩,不是則返回404
if (request.headers['referer'].indexOf('http://sherry.com:9999') === 0) {
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
const string = `window[{{xxx}}] = {{data}}`
const data = fs.readFileSync('./public/friend.json').toString()
// 使用獲取到的隨機(jī)函數(shù)名query.functionName替換friend.js內(nèi)的函數(shù)名占位符{{xxx}}
string2 = string.replace('{{data}}', data).replace('{{xxx}}', query.callback)
response.write(string2)
response.end()
} else {
response.statusCode = 404
response.end()
}
}
// sherry.com用于引用friend.js以獲取friend.json內(nèi)容的sherry.js設(shè)置
// 方法1:JS動(dòng)態(tài)引用
const script = document.createElement('script')
// 通過(guò)functionName=${random}傳入隨機(jī)函數(shù)名
const script = document.createElement('script')
script.src = `http://qq.com:8888/friend.js?functionName=${random}`
// 執(zhí)行完畢后刪除多余的script,保證頁(yè)面整潔
script.onload = () => {
script.remove()
}
document.body.appendChild(script)
// 方法2:HTML靜態(tài)引用
// 在sherry.js對(duì)應(yīng)的index.html頁(yè)面設(shè)置
<script src="http://qq.com:8888/friend.js"></script>
// 在sherry.js中設(shè)置蘑拯,用以監(jiān)聽內(nèi)容是否獲取成功
console.log(window.xxx)
代碼封裝jsonp() :
function jsonp(url) {
return new Promise((resolve, reject) => {
const random = Math.random()
window[random] = data => {
resolve(data);
};
const script = document.createElement("script")
script.src = `${url}?callback=${random}`
script.onload = () => {
script.remove();
};
script.onerror = () => {
reject();
};
document.body.appendChild(script);
});
}
// jsonp的使用
jsonp('http://qq.com:8888/friend.js')
.then((data) => {
console.log(data)
})
3.4 缺陷及解決
任意網(wǎng)站都可以通過(guò)引用JS獲取域內(nèi)數(shù)據(jù)钝满,可以通過(guò)referer檢查來(lái)避免這個(gè)缺陷
// 檢查是否是允許的網(wǎng)站,是則放行申窘,不是則返回404
if (request.headers['referer'].indexOf('http://sherry.com:9999') === 0) {
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
const string = fs.readFileSync('./public/friend.js').toString()
const data = fs.readFileSync('./public/friend.json').toString()
string2 = string.replace('{{data}}', data)
response.write(string2)
response.end()
} else {
response.statusCode = 404
response.end()
}