一、什么是跨域
url的組成
JavaScript出于安全方面的考慮,不允許跨域調用其他頁面的對象。那什么是跨域呢,簡單地理解就是因為JavaScript同源策略的限制,a.com域名下的js無法操作b.com或是c.a.com域名下的對象。
當協議、子域名、主域名、端口號中任意一個不相同時,都算作不同域。不同域之間相互請求資源,就算作“跨域”。
例如:http://www.abc.com/index.html請求http://www.efg.com/service.php。
有一點必須要注意:跨域并不是請求發不出去,請求能發出去,服務端能收到請求并正常返回結果,只是結果被瀏覽器攔截了。之所以會跨域,是因為受到了同源策略的限制,同源策略要求源相同才能正常進行通信,即協議、域名、端口號都完全一致。
image
特別說明兩點:
第一:如果是協議和端口造成的跨域問題“前臺”是無能為力的。
第二:在跨域問題上,域僅僅是通過“URL的首部”來識別而不會根據域名對應的IP地址是否相同來判斷。“URL的首部”可以理解為“協議, 域名和端口必須匹配”。
二、什么是同源策略及其限制
同源策略限制從一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的關鍵的安全機制。它的存在可以保護用戶隱私信息,防止身份偽造等(讀取Cookie)。
同源策略限制內容有:
Cookie、LocalStorage、IndexedDB 等存儲性內容
DOM 節點
AJAX 請求不能發送
但是有三個標簽是允許跨域加載資源:
1.<img?src=XXX>?2.<link?href=XXX>?3.<script?src=XXX>
接下來我們討論下有哪些處理跨域的方法。但所有的跨域都必須經過信息提供方的允許。如果未經允許即可獲取,那是瀏覽器同源策略出現漏洞。
三、處理跨域方法一——JSONP
1.JSONP原理
利用<script>元素的這個開放策略,網頁可以得到從其他來源動態產生的 JSON 數據。JSONP請求一定需要對方的服務器做支持才可以。
2.JSONP和AJAX對比
JSONP和AJAX相同,都是客戶端向服務器端發送請求,從服務器端獲取數據的方式。但AJAX屬于同源策略,JSONP屬于非同源策略(跨域請求)
3.JSONP優缺點
JSONP優點是兼容性好,可用于解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持get方法具有局限性。
4.JSONP的流程(以第三方API地址為例,不必考慮后臺程序)
聲明一個回調函數,其函數名(如fn)當做參數值,要傳遞給跨域請求數據的服務器,函數形參為要獲取目標數據(服務器返回的data)。
創建一個<script>標簽,把那個跨域的API數據接口地址,賦值給script的src,還要在這個地址中向服務器傳遞該函數名(可以通過問號傳參:?callback=fn)。
服務器接收到請求后,需要進行特殊的處理:把傳遞進來的函數名和它需要給你的數據拼接成一個字符串,例如:傳遞進去的函數名是fn,它準備好的數據是fn([{"name":"jianshu"}])。
最后服務器把準備的數據通過HTTP協議返回給客戶端,客戶端再調用執行之前聲明的回調函數(fn),對返回的數據進行操作。
<script?type="text/javascript">???
?function?fn(data)?{??????
? alert(data.msg);???
?}
</script>
?<script?type="text/javascript"?src="http://crossdomain.com/jsonServerResponse?jsonp=fn"></script>
其中 fn 是客戶端注冊的回調的函數,目的獲取跨域服務器上的json數據后,對數據進行在處理。
最后服務器返回給客戶端數據的格式為:
fn({?msg:'this??is??json??data'})
5.jQuery的jsonp形式
JSONP都是GET和異步請求的,不存在其他的請求方式和同步請求,且jQuery默認就會給JSONP的請求清除緩存。
$.ajax({
url:"http://crossdomain.com/jsonServerResponse",
dataType:"jsonp",
type:"get",//可以省略
?jsonpCallback:"fn",//->自定義傳遞給服務器的函數名,而不是使用jQuery自動生成的,可省略
jsonp:"jsonp",//->把傳遞函數名的那個形參callback變為jsonp,可省略
success:function?(data){
console.log(data)
;}
?});
四、處理跨域方法二——CORS
1.CORS原理
整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。因此,實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。
2.CORS優缺點
CORS要求瀏覽器(>IE10)和服務器的同時支持,是跨域的根本解決方法,由瀏覽器自動完成。
優點在于功能更加強大支持各種HTTP Method,缺點是兼容性不如JSONP。
只需要在服務器端做一些小小的改造即可:
header("Access-Control-Allow-Origin:*");
?header("Access-Control-Allow-Methods:POST,GET");
例如:網站http://localhost:63342/頁面要請求http://localhost:3000/users/userlist頁面,userlist頁面返回json字符串格{name: 'Mr.Cao', gender: 'male', career: 'IT Education'}
//在服務器端設置同源策略地址?router.get("/userlist",?function?(req,?res,?next)?{?????
?????? var?user?=?{name:?'Mr.Cao',?gender:?'male',?career:?'IT?Education'};
?????? res.writeHeader(200,{"Access-Control-Allow-Origin":'http://localhost:63342'});
?????? res.write(JSON.stringify(user));
?????? res.end();
?? });
在響應頭上添加Access-Control-Allow-Origin屬性,指定同源策略的地址。同源策略默認地址是網頁的本身。只要瀏覽器檢測到響應頭帶上了CORS,并且允許的源包括了本網站,那么就不會攔截請求響應。
五、處理跨域方法三——WebSocket
Websocket是HTML5的一個持久化的協議,它實現了瀏覽器與服務器的全雙工通信,同時也是跨域的一種解決方案。WebSocket和HTTP都是應用層協議,都基于 TCP 協議。但是 WebSocket 是一種雙向通信協議,在建立連接之后,WebSocket 的 server 與 client 都能主動向對方發送或接收數據。同時,WebSocket 在建立連接時需要借助 HTTP 協議,連接建立好了之后 client 與 server 之間的雙向通信就與 HTTP 無關了。
原生WebSocket API使用起來不太方便,我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。
//前端代碼:
<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()?{???
?//?監聽服務端消息???
?socket.on('message',?function(msg)?{
??????? console.log('data?from?server:?--->?'?+?msg);
????? });?
//?監聽服務端關閉????
socket.on('disconnect',?function()?{
???????? console.log('Server?socket?has?closed.');
????? });
?});
?document.getElementsByTagName('input')[0].onblur?=?function()?{
??? socket.send(this.value);
};
</script>
//Nodejs?socket后臺:
var?http?=?require('http');
var?socket?=?require('socket.io');
?//?啟http服務
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...');
?//?監聽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.');
????? });
?});
六、處理跨域方法四——postMessage
如果兩個網頁不同源,就無法拿到對方的DOM。典型的例子是iframe窗口和window.open方法打開的窗口,它們與父窗口無法通信。HTML5為了解決這個問題,引入了一個全新的API:跨文檔通信 API(Cross-document messaging)。這個API為window對象新增了一個window.postMessage方法,允許跨窗口通信,不論這兩個窗口是否同源。postMessage方法的第一個參數是具體的信息內容,第二個參數是接收消息的窗口的源(origin),即"協議 + 域名 + 端口"。也可以設為*,表示不限制域名,向所有窗口發送。
接下來我們看個例子:
http://localhost:63342/index.html頁面向http://localhost:63342/index.html傳遞“跨域請求信息”
//發送信息頁面
?http://localhost:63342/index.html
<html?lang="en">
? <head>
?????? <meta?charset="UTF-8">
?????? <title>跨域請求</title>
??? </head>
?? <body>
?????? <iframe?src="http://localhost:3000/users/reg"?id="frm"></iframe>
????? <input?type="button"?value="OK"?onclick="run()">
?? </body>
?? </html>
?? <script>
????? function??run(){
???????? var?frm=document.getElementById("frm");
????????? frm.contentWindow.postMessage("跨域請求信息","http://localhost:3000");
????? }
?? </script>
//接收信息頁面
http://localhost:3000/message.html
?window.addEventListener("message",function(e){
?//通過監聽message事件,可以監聽對方發送的消息。??
console.log(e.data);?
?},false);
vue跨域
1、fetch
App.vue
//跨域請求處理
created(){
//fetch
fetch("/apis/server_new/login",{
method:"post",
header:{
// "Content-type":"application/json",
token:"54cas56156d4a6s1das1d6sa4d6a51d65asd"
},
body:JSON.stringify({username:"admin",password:"123456"})
})
.then(result=>{
// console.log(result)
return result.json()
})
.then(data=>{
console.log(data)
})
}
在config/index.js
// Paths
? ? assetsSubDirectory: 'static',
? ? assetsPublicPath: '/',
? ? proxyTable: {
? ? '/apis': {? ? //將www.exaple.com印射為/apis
? ? ? ? ? ? target: 'https://www.exaple.com',? // 接口域名
? ? ? ? ? ? secure: false,? // 如果是https接口,需要配置這個參數
? ? ? ? ? ? changeOrigin: true,? //是否跨域
? ? ? ? ? ? pathRewrite: {
? ? ? ? ? ? ? ? '^/apis': ''? //需要rewrite重寫的,
? ? ? ? ? ? }? ? ? ? ? ? ?
? ? ? ? }
? ? },
重啟:必須重啟
2、axios
安裝axios
npm install acios
引入axios
import axios from 'axios'
Vue.prototype.$axios=axios
實現
//axios
this.$axios.post("/apis/server_new/login",{username:"admin",password:"123456"})
.then(data=>{
console.log(data)
})
設置token
axios.defaults.header.common['token']="5sad5as5da5as5d5asd564d5"
axios.defaults.header.post['Content-type']="application/json"