同源策略
說到跨域就離不開瀏覽器的同源策略
- 協議
- 域名
- 埠號
只要有一個不同,就是跨域
非同源下
非同源下,三種行為會受到限制
- Cookie、LocalStorage 和 IndexDB 無法讀取。
- DOM 無法獲得
- AJAX 請求不能傳送
跨域最常用的方案
- jsonp(只能傳送get請求)
- cors
- postMessage
- document.domain
- window.name
- location.hash
- http-proxy
- nginx
- websocket
自己實現一個jsonp
jsonp實際上返回的是一個函式呼叫
function jsonp(url,data,callback){
var scriptObj = document.createElement('script');
var head = document.getElementsByTagName('head')[0];
var fnName = 'jquery_'+new Date().getTime();
var arr = []
for(var key in data){
console.log(key)
arr.push(key+'='+data[key]);
}
data = arr.join('&');
window[fnName] = function(json){
callback(json);
}
scriptObj.src = url + '?' + data + '&'+callback+'='+fnName;
head.appendChild(scriptObj);
}
jsonp('http://10.252.55.52:1337/user/socialInfo',{
page:1,
pageList:5,
type:1
},function(data){
console.log(data)
})
function jsonp({url,params,cb}){
return new Promise((resolve,reject)=>{
window[cb] = function(data){
resolve(data)
}
params = {...params,cb}
let arrs = [];
for(let key in params){
arrs.push(`${key}=${params[key]}`)
}
let script = document.createElement('script')
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}
jsonp({
url:'',
params:{},
cb:'show'
}).then(data=>{
console.log(data)
})
複製程式碼
cors
cors方式解決跨域問題,往往是需要在服務端設定一些請求頭
我們可以通過 req.headers.origin 獲取origin
下面是常用的一些請求頭的設定
setHeader('Access-Control-Allow-Origin':origin) //允許哪個源可以訪問我
setHeader('Access-Control-Allow-Headers':) //允許攜帶哪個頭訪問我
setHeader('Access-Control-Expose-Headers':) //允許返回的頭
setHeader('Access-Control-Allow-Methods':) //響應標頭指定響應訪問所述資源到時允許的一種或多種方法
setHeader('Access-Control-Max-Age':) //用來指定本次預檢請求的有效期,單位為秒
setHeader('Access-Control-Allow-Credentials':true)
postMessage
postMessage()方法允許來自不同源的指令碼採用非同步方式進行有限的通訊,可以實現跨文字檔、多視窗、跨域訊息傳遞。
舉例
假如有一個頁面a.html 位於 www.test.com 在a.html 通過iframe嵌入b.html,b.html位於www.domain.com下
<iframe src="http://www.domain.com/b.html" frameborder="0" id="frame"></iframe>
複製程式碼
如果a.hhtml想和iframe通訊的話,可以如下操作
let frame = doucument.getElementById('frame') //想和誰通訊,就先獲取這個視窗,此時我們需要的iframe裡的window
frame.contentWindow.postMessage('你好啊',http://www.domain.com) //傳送訊息,後面要寫明是給哪個域發的
複製程式碼
在b.html接收
wondiw.onmessage = function(e){
console.log(e.data)
e.source.send('我不好',e.origin) //再回復訊息
}
或者
document.addEventListener('message', function(e){
// content.....
}
)
複製程式碼
document.domain
利用document.domain 實現跨域:
前提條件:這兩個域名必須屬於同一個一級域名,而且所用的協議,埠都要一致,否則無法利用document.domain進行跨域.
例如:
www.sojson.com 下指到sojson.com 是可以的。
icp.sojson.com 下指到 sojson.com 是可以的。
但是
www.sojson.com 下指到 www.baidu.com 是不行的。
sojson.com 指到 baidu.com 還是不行的
如果我們要在當前頁面下,“www.sojson.com/shiro” 下上傳圖片到 "cdn.sojson.com/images/" 下去
需要在兩個頁面都寫上
if(document.domain !='sojson.com'){
document.domain = 'sojson.com';
}
複製程式碼
window.name
對於這個方案我的理解是
有一個a.html 位於www.test.com下
有一個b.html 位於www.domain.com下
兩個頁面想通訊的話
我們需要在a.html頁面引入一個iframe
<iframe src="http://www.domain.com/b.html" frameborder="0" id="frame" onload="load"></iframe>
let first = true //是否第一次載入
function load(){
if(first){
let iframe = document.getElementById('frame')
iframe.src='http://www.test.com/b.html' //最後必須把b.html設定為和a.html同域名的,這樣才能獲取iframe.contentWindow.name,不然會受同源策略的影響
first = false
}else{
console.log(iframe.contentWindow.name)
}
}
複製程式碼
iframe.contentWindow.name就可以獲取到這個資料了
在b.html我們可以把想傳遞的資料放到 window.name上
window.name='你好啊'
location.hash
有一個a.html 位於www.test.com下
有一個c.html 位於www.domain.com下
現在a.html想訪問c.html
a.html
<iframe src="http://www.domain.com/c.html#iloveyou"></iframe>
wondiw.onhashchange = function(e){
}
或者
document.addEventListener('hashchange', function(e){
// content.....
}
)
複製程式碼
c.html
console.log(window.location.hash) //獲取這個hash值
let iframe = document.createElement('iframe')
iframe.src='http://www.test.com/b.html#idontloveyou"'
document.body.appendChild(iframe)
複製程式碼
b.html
window.parent.parent.location.hash = location.hash
複製程式碼
websocket
websocket屬於高階api,平常我們一般都使用socket.io去進行相容
服務端需要先安裝ws
服務端程式碼如下:
let WebSocket = require('ws')
let wss = new WebSocket.Server({port:3000})
wss.on('connection',function(ws){
ws.on('message',function(data){ //服務端接收訊息
console.log(data)
ws.send('我不好') //服務端傳送訊息
})
})
複製程式碼
客戶端程式碼如下:
let socket = new WebSocket('ws://localhost:3000')
socket.onopen = function(){
socket.send('你好啊')
}
socket.onmessage = function(e){
console.log(e.data) // 客戶端接收訊息
}
複製程式碼
nginx
首先安裝nginx
如果是mac的話
brew install nginx //安裝nginx
nginx.conf //配置檔案
下面是一些常用的配置
add_header "Access-Control-Allow-origin" "*"