前言
定義
瀏覽器為了保證用戶信息的安全,防止惡意網(wǎng)站竊取數(shù)據(jù),禁止不同域之間的js交互。對(duì)于瀏覽器而言,只要協(xié)議、域名、端口其中有一個(gè)不同就會(huì)觸發(fā)同源策略,造成跨域,從而限制交互
cookie、storage、indexDB等不能獲取
ajax不能發(fā)送請(qǐng)求、dom樹無法獲得
為什么要限制跨域訪問
如果一個(gè)網(wǎng)頁可以隨意的訪問另一個(gè)網(wǎng)站的資源,就有可能在用戶完全不知情的情況下出現(xiàn)安全問題
瀏覽器出于安全問題,對(duì)同源請(qǐng)求放行,對(duì)異源請(qǐng)求限制,這些限制規(guī)則統(tǒng)稱為同源策略,因?yàn)橄拗圃斐傻拈_發(fā)問題,稱之為跨域(異源)問題
對(duì)標(biāo)簽發(fā)出的跨域請(qǐng)求輕微限制,對(duì) AJAX 發(fā)出的跨域請(qǐng)求嚴(yán)厲限制
方法
常用方法
跨域資源共享(CORS)
CORS(Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問跨域資源時(shí),瀏覽器與服務(wù)器應(yīng)該如何溝通。CORS背后的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進(jìn)行溝通,從而決定請(qǐng)求或響應(yīng)是應(yīng)該成功還是失敗。
是一套機(jī)制,用于瀏覽器校驗(yàn)請(qǐng)求。只要服務(wù)器明確表示允許,則校驗(yàn)通過;服務(wù)器明確拒絕或沒有表示,則校驗(yàn)不通過。CORS將請(qǐng)求分為兩類,簡單請(qǐng)求和預(yù)檢請(qǐng)求:
簡單請(qǐng)求
請(qǐng)求方法為GET、HEAD、POST
頭部字段滿足CORS安全規(guī)范
請(qǐng)求頭的Content-Type為 text/plain、multipart/form-data、application/x-www-form-urlencoded
服務(wù)器響應(yīng)頭,要么同源,要么放行
預(yù)檢請(qǐng)求
瀏覽器發(fā)送預(yù)請(qǐng)求
從哪個(gè)源發(fā)送的請(qǐng)求:Origin:http://my.com
請(qǐng)求方式:Access-Controt-Request-Method:POST
請(qǐng)求改變了哪些請(qǐng)求頭:Access-Controt-Request-Headers:content-type
服務(wù)器進(jìn)行響應(yīng)
Access-Controt-Allow-Origin:http://my.com...,允許的源,可以多個(gè)
Access-Controt-Allow-Method:POST,GET...,允許的方式,可以多個(gè)
Access-Controt-Allow-Headers:content-type...,允許改變的請(qǐng)求頭,可以多個(gè)
Access-Controt-Max-Age:86400,多少秒不用再次檢驗(yàn)
jsonp
前端寫一個(gè)函數(shù),服務(wù)端調(diào)用函數(shù)傳入?yún)?shù),通過參數(shù)獲取,根本不是AJAX
// 獲取返回結(jié)果
function callback(resp){
console.log(resp);
}
// 向服務(wù)端請(qǐng)求js文件
function request(url){
// 生成一個(gè) script 標(biāo)簽
const script = document.createElement('script')
// 標(biāo)簽引用 js 路徑為填入路徑
script.src = url
// 標(biāo)簽解析完成之后刪除
script.onload = function(){
script.remove()
}
// 插入 body 中
document.body.appendChild(script)
}
Nginx代理跨域
差別
cors:需要服務(wù)器設(shè)置響應(yīng)頭
jsonp:需要服務(wù)器響應(yīng)一段js代碼,并且還要調(diào)用函數(shù)
方法
瀏覽器請(qǐng)求自己的服務(wù)器 proxy
代理服務(wù)器請(qǐng)求目標(biāo)服務(wù)器 target
服務(wù)器之間沒有跨域問題,目標(biāo)服務(wù)器響應(yīng)代理服務(wù)器
代理服務(wù)器返回?cái)?shù)據(jù)
// 引入庫
const express = require('express')
// 創(chuàng)建服務(wù)器
const app = express()
// 接受對(duì)路徑 /hero 的 GET 請(qǐng)求
app.get('/hero',async (req,res)=>{
// 使用 cors 解決對(duì)代理服務(wù)器的跨域
res.header('assess-control-allow-origin','*')
// 響應(yīng)一段測(cè)試文本
res.send('你好,我是代理服務(wù)器')
// 在這里請(qǐng)求目標(biāo)服務(wù)器,然后返回給前端
})
// 監(jiān)聽9527端口
app.listen(9527,()=>{
console.log('服務(wù)器已啟動(dòng)');
})
擴(kuò)展學(xué)習(xí)
Node中間件代理跨域
Vue項(xiàng)目一般使用此方法,就是對(duì)代理服務(wù)器的一種封裝。
在vue.config.js文件中配置
module.export = {
...
devServer: {
proxy: {
[ process.env.VUE_APP_BASE_API ]: {
target: 'http://xxxx',? ? // 代理跨域目標(biāo)接口
ws: true,
changeOrigin: true,
pathRewrite: {
[ '^' + process.env.VUE_APP_BASE_API ] : ''
}
}
}
}
Node/Express中配置:
const express = require(\'express\')
const proxy = require('http-proxy-middleware')
const app = express()
app.use('/', proxy({
// 代理跨域目標(biāo)接口
target: 'http:? ? // xxxx:8080',
changeOrigin: true,
// 修改響應(yīng)頭信息,實(shí)現(xiàn)跨域并允許帶cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://xxxx')
res.header('Access-Control-Allow-Credentials', 'true')
},
// 修改響應(yīng)信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com' ? ? // 可以為false,表示不修改
}));
app.listen(3000);
Websocket
WebSocket是HTML5標(biāo)準(zhǔn)中的一種基于TCP但是區(qū)別于HTTP的通信協(xié)議,因?yàn)槭情L連接,該協(xié)議不實(shí)行同源政策。以ws://(非加密)和wss://(加密)作為協(xié)議前綴。
因?yàn)閃ebSocket請(qǐng)求頭信息中有Origin字段,表示請(qǐng)求源來自哪個(gè)域,服務(wù)器可以根據(jù)這個(gè)字段判斷是否允許本次通信。
// 創(chuàng)建websocket
var socket = new WebSocket('ws://www.baidu.com');
// 發(fā)送消息
socket.send('hello WebSocket');
// 接收消息
socket.onmessage = function(event){
var data = event.data;
}
postMessage
是HTML5引入的一個(gè)全新的API:跨文檔通信 API(Cross-document messaging)。 這個(gè)API為window對(duì)象新增了一個(gè)window.postMessage方法,允許跨窗口通信,不論這兩個(gè)窗口是否同源。
窗口之間傳遞消息
// 父窗口發(fā)起
window.opener.postMessage("我是來自a頁面的","http://b.com")
// 子窗口接收
window.onmessage = function(e){
e = e || event;
console.log(e.data);//我是來自a頁面的
}
// 子窗口發(fā)送
window.opener.postMessage('我是來自b頁面的', 'http://a.com');
讀取其他窗口LocalStorage
// 父窗口調(diào)用方法
var obj = { name: 'Jack' };
// 存入對(duì)象
window.parent.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://b.com');
// 讀取對(duì)象
window.parent.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
window.onmessage = function(e) {
? ? // 判斷消息是否來自a窗口
? ? if (e.origin != 'http://a.com') return;
// "Jack"
console.log(JSON.parse(e.data).name);
}
b窗口接收消息,獲取自己的localStorage之后再傳遞給a窗口
window.onmessage = function(e) {
? // 消息來自自己,不予理會(huì)
if (e.origin !== 'http://b.com') return;
var payload = JSON.parse(e.data);
// 判斷方法進(jìn)行操作
switch (payload.method) {
case 'set':
localStorage.setItem(payload.key, JSON.stringify(payload.data));
break;
case 'get':
var parent = window.parent;
var data = localStorage.getItem(payload.key);
? ? ? // 如果是獲取,獲取之后傳遞給a窗口
parent.postMessage(data, 'http://a.com');
break;
case 'remove':
localStorage.removeItem(payload.key);
break;
}
}
單向跨域
利用瀏覽器對(duì)標(biāo)簽發(fā)出的跨域請(qǐng)求輕微限制,進(jìn)行單方面的跨域請(qǐng)求
<img src='' />標(biāo)簽發(fā)送get請(qǐng)求
<script src=""></script>標(biāo)簽嵌入跨域腳本,語法錯(cuò)誤信息只能在同源腳本中捕捉到。
<link src="">標(biāo)簽嵌入CSS。由于CSS的松散的語法規(guī)則,CSS的跨域需要一個(gè)設(shè)置正確的Content-Type消息頭。
<video>、<audio>嵌入多媒體資源。
<object>、<embed>、<applet>的插件。
@font-face引入的字體。一些瀏覽器允許跨域字體( cross-origin fonts),一些需要同源字體(same-origin fonts)。文章來源:http://www.zghlxwxcb.cn/news/detail-499800.html
<iframe> 和 <iframe>載入的任何資源。站點(diǎn)可以使用X-Frame-Options消息頭來阻止這種形式的跨域交互。文章來源地址http://www.zghlxwxcb.cn/news/detail-499800.html
到了這里,關(guān)于js跨域問題及解決方法匯總的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!