一 什么是跨域?
主要是由於瀏覽器的同源策略引用的,同源策略是瀏覽器的安全機制,當協議,域名,端口三者有一個不同,瀏覽器就禁止訪問資源。
如下 url 上的源是:http://www.company.com:80
如果地址里面的協議,域名,端口號都相同就是屬於同源的。
* http://www.a.com/dir/page.html ----成功
* http://www.child.a.com/test/index.html ----失敗,域名不同
* https://www.a.com/test/index.html ----失敗,協議不同
* http://www.a.com:8080/test/index.html ----失敗,端口號不同
不受同源策略限制的有:
* 頁面中的連接,重定向以及表單的提交是不會收到同源策略的影響的;
* 跨域資源的引入是可以的,但是js不能讀寫加載的內容,如嵌套到頁面中的<script src='....'></script>,<img>,<link>,<iframe>
嚴格的來講:瀏覽器並不是全部禁止跨域資源的請求,它只是禁止對跨越資源的讀操作。瀏覽器的同源限制策略是這樣的:
* 瀏覽器允許跨域寫操作,如連接,重定向;
* 瀏覽器允許跨域資源嵌入,如img,script標簽。
* 瀏覽器不允許跨域讀操作
二 解決跨域的方法?
最常用的解決跨域的常用的方法有JSONP,CORS
(1)使用JSONP來解決跨域
實現原理:a.com/jsonp.html 想要得到 b.com/main.js 里面的數據,可以在 jsonp.html 里面創建一個回調函數 xxx,動態的添加 <script>元素,向服務器發送請求,請求地址后面提那就上查詢的字符串,通過回調函數callback 參數指定回調函數的名稱,請求地址為 http://b.com/main.js?callbcak=xxx,在main 里面調用這個回調函數xxx ,並且一JSON數據形式作為參數傳遞,完成回調
// jsonp.html // 創建一個script標簽 function addScriptTag(src){ var script=document.createElement('script'); script.setAttribute('type','text/javascript'); script.src=src document.body.appendChild(script); } // 頁面加載完畢后創建一個script標簽 window.onload=function(){ addScriptTag('http://b.com/main.js?callback=foo'); } function foo(data){ console.log(data.name+'歡迎你') }
main.js里面的代碼:
foo({name:'張三'})
JSONP 方法的缺點:
* 使用這種方法,只要是個網站都可以拿到 b.com 里面的數據,存在着安全性的問題;
* 只能發送get 請求,不能發送 POST 請求;
* 可能會被注入惡意代碼,篡改頁面內容,可以采用字符串過濾來規避此問題。
(相關鏈接:)
(2)使用CORS 來解決此方法
CORS 是一個W3C標准,全稱是跨域資源共享,它允許瀏覽器向跨域資源服務器,發起 XMLHttpRequest 請求,從而克服了AJAX 只能同源使用的限制。
在剛才的列子里面,可以在 b.com 里面添加響應頭允許 a.com 的訪問,代碼如下:
Access-Control-Allow-Origin:http://a.com
然后a.com 就可以用ajax 獲取 b.com 里的數據了。
三 webpack解決跨域?
webpack proxy ,就是 webpack 提供的解決跨域的方案。
其基本行為是接受客戶端發送的請求后轉發給其他的服務器,目的是為了便於開發者在開發的模式下解決跨域的問題。
要想實現代理必須要一個中間服務器, webpack 提供服務器的工具是 webpack-dev-server,只適用於開發階段。
可以在webpack 配置對象屬性中通過 devServer 屬性來配置:如下
// ./webpack.config.js const path = require('path') module.exports = { // ... devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, port: 9000, proxy: { '/api': { target: 'https://api.github.com' } } // ... } }
devServer 里面的 proxy 就是關於代理的配置,該屬性是一個對象,對象中的每一個規則就是一個代理的規則匹配,屬性的名稱是需要被代理的請求路徑前綴,一般為了辨別都會被設置為 /api ,值為對象的代理匹配規則,對應如下:
* target : 表示的是代理到的目標地址
* pathRewrite: 默認情況下,我們的 /api-hy 也會被寫到 RUL 中,如果希望刪除,可以使用 pathRewrite
* secure :默認情況下不接受轉發到 https 的服務器上的,如果希望支持,可以設置為 false
* changeOrigin: 它是表示是否更新代理后請求的 headers 中的 host 地址
四 工作原理
proxy 工作原理上市利用 http-proxy-middleware 這個http 代理中間件,實現請求轉發給其他的服務器。如下:在開發階段,本地地址是 Http://loaclhost:3000 , 該瀏覽器發送一個前綴帶有 /api 標識的向服務器請求數據,但是這個服務器只是將這個請求轉發給另一台服務器:
const express = require('express'); const proxy = require('http-proxy-middleware'); const app = express(); app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true})); app.listen(3000); // http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
在開發階段,webpack-dev-server 會自動啟動一個本地開發服務器,所以我們的應用在開發階段是獨立運行在 localhost 的一個端口上的,而后端服務器又是運行在另一個地址上
所以在開發階段中,由於瀏覽器的同源策略,當本地訪問的時候就會出現跨域資源請求的問題,通過設置 webpack proxy 實現代理請求后,相當於瀏覽器和服務器之間添加了一個代理着。當本地發送請求的時候,中間服務器會接受這個情求,並將這個請求轉發給目標服務器,目標服務器返回數據后,中間服務器又會將數據返回給瀏覽器,當中間服務器將數據返回給服務器的時候,它們兩者是同源的,並不會存在跨域的問題。
服務器和服務器之間是不會存在跨域資源的問題的。
參考文章: