對於前端開發來講跨域應該是最不陌生的問題了,不管是開發過程當中仍是在面試過程當中都是一個常常遇到的一個問題,在開發過程當中遇到這個問題的話通常都是找後端同窗去解決,以致於不少人都忽略了對跨域的認識。爲何會致使跨域?遇到跨域又怎麼去解決呢?本文會對這些問題一一的介紹。javascript
在JavaScript
中,在不一樣的域名下面進行數據交互,就會遇到跨域問題,說到跨域首先要從同源提及,瀏覽器爲了提供一種安全的運行環境,各個瀏覽器廠商協定使用同源策略。css
什麼同源策略
同源策略:同源策略(Same origin policy
)是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,則瀏覽器的正常功能可能都會受到影響。能夠說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。html
同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,瀏覽器很容易受到XSS
、CSRF
等攻擊。所謂同源是指協議+域名+端口
三者相同,即使兩個不一樣的域名指向同一個ip
地址,也非同源。前端
Url組成部分
瞭解同源策略之後,一樣須要對url
的組成部分也順帶了解一下吧,只有瞭解url
以後當出現跨域的時候才知道哪裏出了問題,這樣才能和後端、運維開懟,懟天懟地對空氣。O(∩_∩)O哈哈~其實不是啦,固然是爲了可以和其餘同事能作到良好的溝通,說的的有理有據,以理服人嘛,這纔是王道。vue
從上圖中可以清晰的看出url
中每一個部分的含義:html5
協議
經常使用的協議是http
驗證
,由於明文傳輸用戶名和密碼,非HTTPS
環境下很不安全,通常用的很是少。主機地址
,能夠是域名,也能夠是IP地址端口
http協議默認端口是:80端口,若是不寫默認就是:80端口路徑
網絡資源在服務器中的指定路徑查詢字符串
若是須要從服務器那裏查詢內容,在這裏編輯哈希
網頁中可能會分爲不一樣的片斷,若是想訪問網頁後直接到達指定位置,能夠在這部分設置項目過程過程當中常常會用到一些緩存,瀏覽器爲了網頁的安全在緩存的時候,因爲同源策略的問題對其緩存內容進行了限制,其實想一想也是對的,若是不使用同源策略的話,很容易沖掉緩存的東西。java
Cookie
、LocalStorage
和IndexDB
等沒法讀取。DOM
沒法得到。AJAX
請求不能發送。固然瀏覽器也沒有把全部的東西都限制了,好比圖片、互聯網資源等仍是容許跨域請求的。容許跨域請求都是使用標籤,只有三個標籤是容許跨域加載資源:webpack
<img src=XXX>
<link href=XXX>
<script src=XXX>
在項目開發過程當中時不時的就會遇到下面這樣拋出了錯誤,有的人可能在開發過程當中沒有遇到過,若是是的話,你可能遇到一個很不錯的後端或者運維。ios
XMLHttpRequest cannot loadhttp://www.******.com/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
上面的報錯就是典型的跨域報錯,既然跨域這麼常見到底都有哪些狀況會致使跨域的問題:nginx
說明 | 是否容許通訊 |
---|---|
同一域名下 | 容許 |
同一域名下不一樣文件夾 | 容許 |
同一域名,不一樣端口 | 不容許 |
同一域名,不一樣協議 | 不容許 |
域名和域名對應ip | 不容許 |
主域名相同,子域名不一樣 | 不容許 |
同一域名,不一樣二級域名 | 不容許 |
不一樣域名 | 不容許 |
跨域解決方案
因爲瀏覽器的限制形成了不少的跨域問題,一樣也是爲了安全,既然出現了跨域就一定要有一些對應的解決方案,總不能遇到跨域以後項目就不作了啊,可能瞬間就涼了。閒話就很少扯了。
JSONP
在遇到跨域的時候常常會說起到的一個詞就是JSONP
,一直在說JSONP
?但是經過什麼原理來實現的呢?我以爲應該瞭解一下到底什麼再去了解一下實現當然原理也就懂得咯。
什麼是JSONP
JSONP:JSON
的一種「使用模式」,可用於解決主流瀏覽器的跨域數據訪問的問題。因爲同源策略,通常來講位於server1.example.com
的網頁沒法與不是server1.example.com
的服務器溝通,而HTML
的<script>
元素是一個例外。利用<script>
元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的JSON
資料,而這種使用模式就是所謂的JSONP
。用JSONP
抓到的資料並非JSON
,而是任意的JavaScript
,用JavaScript
直譯器執行而不是用JSON
解析器解析。 - 選自百度百科
對於JSONP
簡單的百度了一下,百度給出的解釋如上,看完整段話,有一些小的收穫,第一script
標籤具備開放策略,可使用src
的開放性解決其跨域問題。在這裏簡單的闡述一下我的觀點。JSONP
能夠分爲兩個部分來解讀,JSON
與padding
,JSON
當然就不用解釋了,只是一種數據格式,padding
在css
中是內填充的意思,其實JSONP
的原理與內填充有些相似。經過把數據填充js
文件中而後引入到頁面中,並在頁面中使用。
有沒有注意過百度,其實百度的即時搜索就是使用JSONP
來實現的,能夠嘗試一下,在百度中搜索一下,就會在Network
中看到一個以sugrec
爲開頭的請求,這個請求就是使用的JSONP
的形式,爲了你們方便特地截選了一個段鏈接。
鏈接: https://www.baidu.com/sugrec?prod=pc&wd=json&pwd=json&cb=query 返回格式: query({ "q": "json", "p": false, "g": [{ "type": "sug", "sa": "s_1", "q": "json格式" }, { "type": "sug", "sa": "s_2", "q": "jsonp" }, { "type": "sug", "sa": "s_3", "q": "json解析" }, ...] })
經過對百度的即時搜索的分析就能夠簡單的看出JSONP
的實現原理,請求會的js
文件中包含一個函數,其函數名稱就是鏈接中cb
的參數最爲參數傳給後臺,後臺經過處理並在執行這個與參數對應的函數的,當函數執行的時候將把數據以實參的形式傳遞給對應的函數,解決跨域問題。爲了方便閱讀這裏只截取了代碼片斷。
案例:
前端代碼:
$('#btn').click(function(){ var frame = document.createElement('script'); frame.src = 'http://localhost:5000/jsonp?name=aaron&age=18&callback=query'; $('body').append(frame); }); function query(res){ console.log(res.message+res.name+'你已經'+res.age+'歲了'); }
後端代碼:
router.get('/jsonp', (req, res) => { let {name,age,callback} = req.query; let data = {message:'success',name,age}; data = JSON.stringify(data); res.end(`${callback}(${data})`); });
經過如上代碼就能夠簡單的實現JSONP
,雖然JSONP
解決了跨域的問題,仍是有不少弊端的,好比會在頁面中添加一些script
標籤,數據不能雙向操做等等。
使用JSONP
的時候尤爲要注意一點,必定要把插入的script
放到所應用函數的下面。這個和js的執行順序有關係,若是把script
標籤放在上面的話,其方法尚未被讀取在script
標籤中就執行了這個方法一定報錯的,這點很重要哦。
document.domain
document.domain
項目中通常應用的較少,默認狀況下document.domain
存放的是載入文檔的服務器的主機名。能夠在控制檯輸出一下,獲得的則是segmentfault.com
這個域名。我在項目中所用到的則是結合iframe
的時候遇到的跨域,並使用的domain
解決的。
在使用document.domain
實現跨域的時候須要注意一下,這兩個域名必須屬於同一個一級域名!並且所用的協議,端口都要一致,不然沒法利用document.domain
進行跨域。Javascript
出於對安全性的考慮,而禁止兩個或者多個不一樣域的頁面進行互相操做。而相同域的頁面在相互操做的時候不會有任何問題。
簡單的解釋一下,例如想要在www.a.com
中將看到segmentfault.com
中的內容並將其網頁使用iframe
將其嵌入到其網頁中,可是此時瀏覽器是不容許經過JavaScript
直接操做segmentfault.com
的,由於這兩個頁面屬於不一樣的域,在操做以前瀏覽器會檢測是否符合同源策略,若是符合則容許操做,反之則不行。
若想要同過document.domain
實現跨域的話,必須使其知足同源策略,這個時候就須要用到document.domain
,document.domain
都設成相同的域名就能夠了。但要注意的是,document.domain
的設置是有限制的,咱們只能把document.domain
設置成自身或更高一級的父域,且主域必須相同。
例如:
a.com news.a.com
news.a.com
屬於a.com
的一個子域名,按照上面所說已經知足了上面的規則,若是想要實現跨域操做就須要對接子頁面的document.domain
進行操做。
父頁面:
document.domain = 'a.com'; var ifr = document.createElement('iframe'); ifr.src = 'news.a.com/map.html'; ifr.style.display = 'none'; document.body.appendChild(ifr); ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; var oUl = doc.getElementById('ul1'); alert(oUl.innerHTML); ifr.onload = null; };
子頁面:
document.domain = 'a.com'; $ajax.get({ // ...省略 })
其實現原理就是經過iframe
載入一個與你想要經過ajax
獲取數據的目標頁面處在相同的域的頁面,因此這個iframe
中的頁面是能夠正常使用ajax
去獲取你要的數據的,而後就是經過咱們剛剛講得修改document.domain
的方法,讓咱們能經過js徹底控制這個iframe
,這樣咱們就可讓iframe
去發送ajax
請求,而後收到的數據咱們也能夠得到了。
location.hash
若理解了document.domain
實現跨域原理,那麼location.hash
也就很號理解了,其原理與document.domain
很類似同樣都是動態插入一個iframe
,而後把iframe
的src
指向服務端地址,而服務端一樣都是輸出一段JavaScript
代碼,一樣都是利用和子窗口之間的通訊完成數據傳輸,一樣要針對同源策略作出處理。
既然說到了hash
到底什麼是hash
這裏也就單獨的說一下吧,雖然很好理解,可是對於新同窗來講可能還不知道hash
具體是什麼?
hash:通常翻譯作散列、雜湊,或音譯爲哈希,是把任意長度的輸入(又叫作預映射pre-image)經過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間一般遠小於輸入的空間,不一樣的輸入可能會散列成相同的輸出,因此不可能從散列值來肯定惟一的輸入值。簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數。 -- 節選自百度百科
讀完以後感受本身整我的都很差了,有些似懂非懂的意思,我所理解的哈希是指一個過程,這個過程就是把任意長度的輸入,經過哈希算法,變換成固定長度的輸出,所輸出的稱爲哈希值。這種變換是一種壓縮映射,也即哈希值所佔的空間通常來講遠小於輸入值的空間,不一樣的輸入可能會哈希出相同的輸出(機率很小)。
哈希有以下特色:
那麼哈希在平時項目開發中有什麼用途呢?能夠用哈希來作什麼事情?對於前端來講用到哈希最多的時候可能就是錨點定位。經過不一樣的哈希值定位到描點指定的元素位置上。
<a href='#1'>red</a> <a href='#2'>black</a> <a href='#3'>yellow</a> <a href='#4'>pink</a> <div id='1' style='width:500px;height:200px;background-color:red'> </div> <div id='2' style='width:500px;height:200px;background-color:black'> </div> <div id='3' style='width:500px;height:200px;background-color:yellow'> </div> <div id='4' style='width:500px;height:1200px;background-color:pink'> </div>
關於更多細節的東西再也不這裏贅述了,若是想要了解更多的話你們能夠自行google
,再說下去的話可能就跑題了。
簡單的介紹了一下哈希與哈希的用處那麼又該如何使用哈希來實現跨域呢?其實很簡單,若是index
頁面要獲取遠端服務器的數據,動態插入一個iframe
,將iframe
的src屬性指向服務端地址。這時top window
和包裹這個iframe
的子窗口因爲同源策略的緣由是不能直接通訊的,因此改變子窗口的路徑就好了,將數據當作改變後的路徑的hash
值加在路徑上,而後就能通訊了,將數據加在index
頁面地址的hash
值上。index
頁面監聽地址的hash
值變化而後作出判斷,處理數據。
父頁面:
<iframe src="http://localhost:7000/b.html#key=1&key1=2"></iframe>
因爲哈希值的改變不會改變網頁的網址的,因此服務端能夠經過獲取到哈希來解析url
中的參數,並把數據返回給前端便可。經過parent.location.hash
去改變哈希值,而後就能夠像document.domain
同樣去獲取到子頁面的數據了。parent.location.hash
該方法是有侷限性的,在IE
和Chrome
中是不支持這種操做的。那麼整個問題應如何解決呢?
在同域的域名下面添加一個*.html
(*表明任意名)文件,而後把經過iframe
把*.html
引入到父頁面中,並把須要請求的接口iframe
添加到*.html
中去請求,這樣就能夠解決了。
http://localhost:6000/a.html
<!DOCTYPE html> <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> <title>無</title> </head> <body> <script type="text/javascript"> function sendRequest(){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = 'http://localhost:7000/b.html#Aaron'; document.body.appendChild(ifr); } function checkHash(){ var data = location.hash?location.hash.substring(1):''; if(data) location.hash = ''; } setInterval(checkHash,1000); window.onload = sendRequest; </script> </body> </html>
http://localhost:7000/b.html
<!DOCTYPE html> <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> <title>無</title> </head> <body> <script type="text/javascript"> function checkHash(){ var data = ''; switch(location.hash){ case '#Aaron': data = 'my Aaron'; break; case '#Angie': data = 'my Angie'; break; default : break; } data && callBack('#'+data); } function callBack(hash){ var proxy = document.createElement('iframe'); proxy.style.display = 'none'; proxy.src = 'http://localhost/c.html'+hash; document.body.appendChild(proxy); } window.onload = checkHash; </script> </body> </html>
http://localhost:6000/c.html
<!DOCTYPE html> <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> <title>無</title> </head> <body> <script type="text/javascript"> parent.parent.location.hash = self.location.hash.substring(1); </script> </body> </html>
a.html
中有一個隱藏的iframe
,該iframe
指向異域http://localhost:7000/b.html
的b.html
,且傳遞hash
值給b.html
`b.html獲取
hash值,生成
data值,而後動態建立
iframe,該
iframe將
data值傳給與
a.html同域的
c.html 由於
c.html與
a.html`同域,能夠傳值當然也就解決了跨域問題。
window.name
window.name
這個屬性不是一個簡單的全局屬性只要在一個window
下,不管url
怎麼變化,只要設置好了window.name
,那麼後續就一直都不會改變,同理,在iframe
中,即便url在變化,iframe
中的window.name
也是一個固定的值,利用這個,咱們就能夠實現跨域了。
http://localhost:6000/a.html
<iframe src="http://localhost:7000/b.html" frameborder="1"></iframe> <script> var ifr = document.querySelector('iframe') ifr.style.display = 'none' var flag = 0; ifr.onload = function () { if (flag == 1) { ifr.contentWindow.close(); } else if (flag == 0) { flag = 1; ifr.contentWindow.location = 'http://localhost:6000/proxy.html'; } } </script>
http://localhost:7000/b.html
var person = { name: 'Aaron', age: 18 } window.name = JSON.stringify(person)
http://localhost:6000/proxy.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>proxy</title> </head> <body> <p>這是proxy頁面</p> </body> </html>
在http://localhost:6000
下有一個a.html
,在http://localhost:7000
下有一個b.html
,在http://localhost:6000/a.html
中建立了一個iframe
標籤並把地址指向了http://localhost:7000/b.html
,在b.html
中的window.name
賦值保存了一段數據,可是如今還獲取不了,由於是跨域的,因此,咱們能夠把src
設置爲當前域的http://localhost:6000/proxy.html
,雖然域名改變了可是window.name
是沒有改變的。這樣就能夠拿到咱們想要的數據了。
postMessage(HTML5)
可能不少不知道postMessage
整個API
,在HTML5
中新增了postMessage
方法容許來自不一樣源的腳本採用異步方式進行有限的通訊,能夠實現跨文本檔、多窗口、跨域消息傳遞,postMessage
在不少瀏覽器中都已經獲得了良好的支持,因此可放心的使用。該方法能夠經過綁定window
的message
事件來監聽發送跨文檔消息傳輸內容。
postMessage()
方法接受兩個參數
JavaScript
的任意基本類型或可複製的對象,然而並非全部瀏覽器都作到了這點兒,部分瀏覽器只能處理字符串參數,因此咱們在傳遞參數的時候須要使用JSON.stringify()
方法對對象參數序列化,在低版本IE
中引用json2.js
能夠實現相似效果。協議+主機+端口號+URL
,URL
會被忽略,因此能夠不寫,這個參數是爲了安全考慮,postMessage()
方法只會將message
傳遞給指定窗口,固然若是願意也能夠建參數設置爲"*",這樣能夠傳遞給任意窗口,若是要指定和當前窗口同源的話設置爲"/"。http://localhost:6000/a.html
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>無</title> </head> <body> <div> <input id="text" type="text" value="My name’s Aaron" /> <button id="send" >發送消息</button> </div> <iframe id="receiver" src="http://localhost:7000/b.html"></iframe> <script> window.onload = function() { var receiver = document.getElementById('receiver').contentWindow; var btn = document.getElementById('send'); btn.addEventListener('click', function (e) { e.preventDefault(); var val = document.getElementById('text').value; receiver.postMessage("Hello "+val+"!", "http://localhost:7000"); }); } </script> </body> </html>
http://localhost:7000/b.html
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>無</title> </head> <body> <div id="message"> Hello World! </div> <script> window.onload = function() { var messageEle = document.getElementById('message'); window.addEventListener('message', function (e) { if (e.origin !== "http://localhost:6000") { return; } messageEle.innerHTML = "從"+ e.origin +"收到消息: " + e.data; }); } </script> </body> </html>
這樣咱們就能夠接收任何窗口傳遞來的消息了,爲了安全起見,咱們利用這時候的MessageEvent
對象判斷了一下消息源,MessageEvent
對象,這個對象中包含不少東西。
message
使用postMessage
方法比以上方法用起來要輕便,沒必要有太多的繁瑣操做,能夠說postMessage
是對於解決跨域來講是一個比較好的解決方案,不會顯得代碼特別的臃腫,而且各個瀏覽器又有良好的支持。
跨域資源共享(CORS)
CORS:全稱"跨域資源共享"(Cross-origin resource sharing
)。CORS
須要瀏覽器和服務器同時支持,才能夠實現跨域請求,目前幾乎全部瀏覽器都支持CORS
,IE
則不能低於IE10
。CORS
的整個過程都由瀏覽器自動完成,前端無需作任何設置,跟平時發送ajax
請求並沒有差別。CORS
的關鍵在於服務器,只要服務器實現CORS
接口,就能夠實現跨域通訊。
跨域資源共享(CORS
) 是一種機制,它使用額外的HTTP
頭來告訴瀏覽器讓運行在一個origin (domain)
上的Web
應用被准許訪問來自不一樣源服務器上的指定的資源。當一個資源從與該資源自己所在的服務器不一樣的域、協議或端口請求一個資源時,資源會發起一個跨域HTTP
請求。在上面說過src
是不受同源策略限制的,可是出於安全緣由,瀏覽器限制從腳本內發起的跨源HTTP
請求。例如,XMLHttpRequest
和FetchAPI
遵循同源策略。這意味着使用這些API
的Web
應用程序只能從加載應用程序的同一個域請求HTTP
資源,除非響應報文包含了正確CORS
響應頭。
全部CORS
相關的的頭都是Access-Control
爲前綴的。下面是每一個頭的一些細節。
字段 | 描述 |
---|---|
Access-Control-Allow-Methods | 該字段必需,它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。注意,返回的是全部支持的方法,而不單是瀏覽器請求的那個方法。這是爲了不屢次"預檢"請求 |
Access-Control-Allow-Headers | 若是瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段,不限於瀏覽器在"預檢"中請求的字段 |
Access-Control-Allow-Credentials | 該字段與簡單請求時的含義相同。 |
Access-Control-Max-Age | 該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即容許緩存該條迴應1728000秒(即20天),在此期間,不用發出另外一條預檢請求。 |
import express from "express"; import cors from "cors"; const app = express(); const corsOptions = { origin: 'http://example.com', optionsSuccessStatus: 200 } app.get('/products/:id', cors(corsOptions), (req, res, next) => { res.json({msg: 'This is CORS-enabled for only example.com.'}) }) app.listen(80, function () { console.log('啓用corba,端口:80') })
使用CORS
簡單請求,很是容易,對於前端來講無需作任何配置,與發送普通ajax
請求無異。惟一須要注意的是,須要攜帶cookie
信息時,須要將withCredentials
設置爲true
便可。CORS
的配置,徹底在後端設置,配置起來也比較容易,目前對於大部分瀏覽器兼容性也比較好,如今應用最多的就是CORS
解決跨域了。
在開發過程當中因爲各個公司的差別選用的接口風格也是不一樣的,不少公司會採用RESTful風格去編寫接口,不免就會有些騷操做,在跨域請求時自定義的請求頭是不容許這樣操做的,由於瀏覽器根據Response Headers
判斷請求是否容許。
跨域時默認容許的方法
由於瀏覽器但願在網頁進行跨域請求操做的時候是保證服務端的安全的,不容許任何隨便進行跨域,不容許隨便的方法進行跨域,以防數據被惡意篡改。因此提供這些限制以後,就能夠進行一些很是有利的判斷。針對如上問題須要服務端進行特殊處理才行
const http = request("http"); http.createServer(function(request,response){ response.writeHead(200,{ // 設置容許跨域的訪問地址 'Access-Control-Allow-Origin':'*', // 設置容許訪問的自定義請求頭 'Access-Control-Allow-Headers':'X-Test-Cors', // 設置容許跨域的methods 'Access-Control-Allow-Methods':'POST,Put,Delete', // 容許以上三個方式進行跨域的最長時間,1000秒內不須要發送預請求驗證了 'Access-Control-Max-Age':'1000' }) response.end('123') }).listen(3000)
這樣設置,這個請求就成功了,可是能夠觀測到多了一個請求,這個多出來的請求就是預請求,告訴瀏覽器,這個自定義請求是容許的,而後再正式的發送這個請求,經過驗證以後就請求成功了,瀏覽器爲何要作這些限制,是由於保證安全,不容許隨便一我的均可以進行跨域。
WebSocket協議跨域
WebSocket
是HTML5
新推出的一個API
,經過WebSocket
能夠實現客戶端與服務端的即時通訊,如聊天室,服務數據同步渲染等等。WebSocket
是點對點通訊,服務端與客戶端能夠經過某種標識完成的。WebSocket
是不受同源策略限制的因此能夠利用這個特性直接與服務端進行點對點通訊。
如下示例沒有使用HTML5
的WebSocket
而是使用的socket.io
完成相似的功能操做。
若若的說一句:其實我一直覺得WebSocket
與Ajax
同樣是受同源策略
限制的,通過學習才發現不是的。真是學到老活到老(關谷口音)。O(∩_∩)O
服務端:
var io = require('socket.io')(1234); io.sockets.on('connection', (client) => { client.on('message', function (msg) { //監聽到信息處理 client.send('服務器收到了信息:' + msg); }); client.on("disconnect", function () { //斷開處理 console.log("client has disconnected"); }); }); console.log("listen 1234...");
客戶端:
$(function () { var iosocket = io.connect('http://localhost:1234/'); var $ul = $("ul"); var $input = $("input"); iosocket.on('connect', function () { //接通處理 $ul.append($('<li>連上啦</li>')); iosocket.on('message', function (message) { //收到信息處理 $ul.append($('<li></li>').text(message)); }); iosocket.on('disconnect', function () { //斷開處理 $ul.append('<li>Disconnected</li>'); }); }); $input.keypress(function (event) { if (event.which == 13) { //回車 event.preventDefault(); console.log("send : " + $input.val()); iosocket.send($input.val()); $input.val(''); } }); });
Websocket
既然能支持跨域方法,那就是說,一個開放給公網的Websocket
服務任何人都能訪問,這樣的話會使數據變得很不安全,因此能夠經過對鏈接域名進行認證便可。
服務器反代
學習路程首先了解了一下什麼是反代,在計算機網絡中,反向代理是代理服務器的一種。服務器根據客戶端的請求,從其關聯的一組或多組後端服務器(如Web服務器)上獲取資源,而後再將這些資源返回給客戶端,客戶端只會得知反向代理的IP
地址,而不知道在代理服務器後面的服務器簇的存在。 -- 節選自百度百科
反向代理服務器:就nginx
把http
請求轉發到另外一個或者一些服務器上。從而輕鬆實現跨域訪問。好比服務器中分別部署了N個服務器,當客戶端發起請求時不用直接請求服務器中N
個節點上的服務,只須要訪問咱們的代理服務器就好了,代理服務器根據請求內容分發到不一樣服務器節點。這僅是一種使用場景,固然還能夠作負載均衡等。
反向代理理解起來不是特別的難,平時生活中最多見的例子,當咱們撥打人工客服的時候,並非直接撥打客服的某一個電話號碼,而是撥打總機號碼,當咱們撥打而後由總機進行處理,而後再分發給不一樣的客服人員。r然而當服務人員須要讓你掛斷電話等待回撥的時候,也不是直接撥打到你的電話,一樣是也經過總機以後再轉發到你的電話。其實這個總機也就至關於反代服務器。雖然這個例子不太貼切可是多多少少就是這個意思。
因爲不太懂Nginx
不知道該如何處理這個部分,只是對反向代理作了一個簡單的瞭解,等之後學習了Nginx
會補上相關代碼。
Nodejs代理跨域
使用Nodejs
進行跨域在我看來,就是使用Node
服務作了一箇中間代理轉發,其原理和反向代理差很少,當訪問某一個URL
時須要經過服務器分發到另外一個服務器URL
地址中。這裏就不過多的贅述了,直接看代碼吧。
示例代碼入下:
main.js
import http from "http"; import httpProxy from "http-proxy"; // 新建一個代理 Proxy Server 對象 const proxy = httpProxy.createProxyServer({}); // 捕獲異常 proxy.on('error', function (err, req, res) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('error'); }); // 在每次請求中,調用 proxy.web(req, res config) 方法進行請求分發 const server = http.createServer((req, res) => { // 在這裏能夠自定義你的路由分發 let host = req.headers.host, ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; switch(host){ case 'www.a.com': proxy.web(req, res, { target: 'http://localhost:3000' }); break; case 'www.b.com': proxy.web(req, res, { target: 'http://localhost:4000' }); break; default: res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Aaron!'); } }); server.listen(8080);
如代碼所示,當訪問www.a.com
的時候,請求就被轉發到了3000
接口上,訪問www.b.com
時就被轉發到了4000
這個接口上。這樣就簡單的完成了一個反向代理的工做。
在使用vue
開發的時候不免也會遇到跨域問題,或許你根本就沒有遇到,可能大家正好處於同一個域裏面,還有一種可能就是,後端同窗或者運維同窗已經處理好有關跨域的相關操做。可是當在開發過程當中遇到跨域的時候,什麼前端應該有對應的解決辦法。vue-cli
是基於Node
服務的,因此咱們能夠利用這個服務來作代理工做,暫時解決開發中的跨域問題。
build/webpack.config.js
module.exports = { devServer: { historyApiFallback: true, proxy: [{ context: '/login', // url以/login爲開頭時啓用代理 target: 'http://www.a.com:8080', // 代理跨域目標接口 changeOrigin: true, secure: false, // 當代理某些https服務報錯時用 cookieDomainRewrite: 'www.b.com' // 能夠爲false,表示不修改 }], noInfo: true } }
在開發過程當中遇到的能夠經過這種方式解決,可是到達生產環境時到底使用什麼方法仍是須要斟酌的,畢竟要使服務數據變得更加的安全才是最好的。
總結
以上講了不少有關跨域的解決方案,有利也有弊,對於我而言可能更加的傾向於後端粑粑或者運維粑粑去解決跨域問題,畢竟前端處理起來畢竟不是很安全,並且後端或者運維處理起來也不是那麼的麻煩。
很感謝你們利用這麼長時間來讀這篇文章,文章中如有錯誤請在下方留言,會盡快作出修改。