成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

「深入淺出」前端開發中常用的幾種跨域解決方案

開發
本文將為大家介紹,前端開發中,最常用的幾種跨域解決方案;

看完本文可以系統地掌握,不同種跨域解決方案間的巧妙,以及它們的用法、原理、局限性和適用的場景

[[342546]]

包括以下幾個方面:

跨域的現象,和幾種常見的跨域表現
跨域的解決方案(原理分析)
修改本地HOST
JSONP
CORS
Proxy
Nginx反向代理
Post Message(利用iframe標簽,實現不同域的關聯)
同源是什么?
如果兩個URL的協議protocol、主機名host和端口號port都相同的話,則這兩個URL是同源。

同源策略
同源策略是一個重要的安全策略。它能夠阻斷惡意文檔,減少被攻擊的媒介。

真實項目中,很少有同源策略,大部分都是非同源策略

跨域是什么?
當協議、域名與端口號中任意一個不相同時,都算作不同域,不同域之間相互請求資源的表現(非同源策略請求),稱作”跨域“。

跨域現象
那么我們就下面的網址分析一下,哪一塊是協議,哪一塊是域名及端口號 

  1. http://kbs.sports.qq.com/index.html 
  2.  
  3. 協議:http(還有以一種https協議) 
  4. 域名:kbs.sports.qq.com 
  5. 端口號:80 
  6.  
  7. https://127.0.0.1:3000 
  8.  
  9. 協議:https 
  10. 域名:127.0.0.1 
  11. 端口號:3000 

假如我們的真實項目開發中的Web服務器地址為 ”http://kbs.sports.qq.com/index.html“,而需要請求的數據接口地址為 "http://api.sports.qq.com/list"。

當Web服務器的地址向數據接口的地址發送請求時,便會造成了跨域現象

造成跨域的幾種常見表現
服務器分開部署(Web服務器 + 數據請求服務器)
本地開發(本地預覽項目 調取 測試服務器的數據)
調取第三方平臺的接口
Web服務器:主要用來靜態資源文件的處理

解決方案

  1. 修改本地HOST(不作介紹) 
  2. JSONP 
  3. CORS 
  4. Proxy 
  5. Nginx反向代理 
  6. Post Message(利用iframe標簽,實現不同域的關聯) 

在后面會詳細分析這四種解決方案的原理和用法配置,以及它們的優點和局限性

  1. 注意: 基于ajax或fetch發送請求時,如果是跨域的,則瀏覽器默認的安全策略會禁止該跨域請求 

  1. 補充說明:以下所有的測試用例,均由Web:http://127.0.0.1:5500/index.html向API:http://127.0.0.1:1001/list發起請求 

API接口的服務器端是自己通過express建立的,下文在服務器端以app.use中間件的形式接受來自客戶端的請求并做處理。

  1. 即 在“http://127.0.0.1:1001/list”from origin“http://127.0.0.1:55”上對XMLHttpRequest的訪問已被CORS策略阻止:被請求的資源上沒有“Access- control - allow-origin”頭 

在后端開啟了一個端口號為1001的服務器之后,我們來實踐一下

  1. let xhr = new XMLHttpRequest; 
  2. xhr.open('get''http://127.0.0.1:1001/list'); 
  3. xhr.onreadystatechange = () => { 
  4.   if (xhr.status === 200 && xhr.readyState === 4) { 
  5.     console.log(xhr.responseText); 
  6.   } 
  7. }; 
  8. xhr.send();  

跨域的常見報錯提示
這就是由于瀏覽器默認的安全策略禁止導致的。

下面介紹一下幾種常見的解決方案。

JSONP
原理:JSONP利用script標簽不存在域的限制,且定義一個全局執行上下文中的函數func

(用來接收服務器端返回的數據信息)來接收數據,從而實現跨域請求。

  1. 弊端: 
  2.  
  3. 只允許GET請求 
  4. 不安全:只要瀏覽器支持,且存在瀏覽器的全局變量里,則誰都可以調用 

圖解JSONP的原理

手動封裝JSONP
callback必須是一個全局上下文中的函數

(防止不是全局的函數,我們需要把這個函數放在全局上,并且從服務器端接收回信息時,要瀏覽器執行該函數)

注意:

uniqueName變量存儲全局的回調函數(確保每次的callback都具有唯一性)
檢驗url中是否含有"?",有的話直接拼接callback,沒有的話補”?“

  1. // 客戶端 
  2. function jsonp(url, callback) { 
  3.   // 把傳遞的回調函數掛載到全局上 
  4.  let uniqueName = `jsonp${new Date().getTime()}`; 
  5.   // 套了一層 anonymous function 
  6.   // 目的讓 返回的callback執行且刪除創建的標簽 
  7.   window[uniqueName] = data => { 
  8.   // 從服務器獲取結果并讓瀏覽器執行callback 
  9.     document.body.removeChild(script); 
  10.     delete window[uniqueName]; 
  11.     callback && callback(data); 
  12.   } 
  13.    
  14.   // 處理URL 
  15.   url += `${url.includes('?')} ? '&' : '?}callback=${uniqueName}'`; 
  16.    
  17.   // 發送請求 
  18.   let script = document.createElement('script'); 
  19.   script.src = url; 
  20.   document.body.appendChild(script); 
  21.  
  22. // 執行第二個參數 callback,獲取數據 
  23. jsonp('http://127.0.0.1:1001/list?userName="lsh"', (result) => { 
  24.  console.log(result); 
  25. }) 
  1. // 服務器端 
  2. // Api請求數據 
  3. app.get('/list', (req, res) => { 
  4.    
  5.   // req.query 問號后面傳遞的參數信息 
  6.   // 此時的callback 為傳遞過來的函數名字 (uniqueName) 
  7.  let { callback } = req.query; 
  8.    
  9.   // 準備返回的數據(字符串) 
  10.   let res = { code: 0, data: [10,20] }; 
  11.   let str = `${callback}($(JSON.stringify(res)))`; 
  12.    
  13.   // 返回給客戶端數據 
  14.   res.send(str); 
  15. }) 

測試用例展示:

客戶端請求的url
服務端返回的數據
返回的callback
返回的數據信息 result

  1. // 服務器請求的 url 
  2. Request URL: 
  3.  http://127.0.0.1:1001/list?userName="lsh"&callback=jsonp159876002 
  4.  
  5. // 服務器返回的函數callback 
  6.  jsonp159876002({"code":0, "data": [10,20]}); 
  7.  
  8. // 客戶端接收的數據信息 
  9. { code: 0, data: Array(2) } 

當瀏覽器發現返回的是jsonp159876002({"code":0, "data": [10,20]});這個函數,會自動幫我們執行的。

JSONP弊端
在上文中說到只要服務器端那里設置了允許通過jsonp的形式跨域請求,我們就可以取回數據。

下面是在我們封裝完jsonp方法之后,向一個允許任何源向該服務器發送請求的網址xxx

  1. jsonp('https://matchweb.sports.qq.com/matchUnion/cateColumns?from=pc', result => { 
  2.   console.log(result); 
  3. }); 

CORS
上文提到,不允許跨域的根本原因是因為Access-Control-Allow-Origin已被禁止

那么只要讓服務器端設置允許源就可以了

原理:解決掉瀏覽器的默認安全策略,在服務器端設置允許哪些源請求就可以了

先來看一下下面的設置有哪些問題

  1. // 服務器端 
  2. app.use((req, res, next) => { 
  3.  // * 允許所有源(不安全/不能攜帶資源憑證) 
  4.  res.header("Access-Control-Allow-Origin""*"); 
  5.  res.header("Access-Control-Allow-Credentials"true); 
  6.  
  7.  /* res.header("Access-Control-Allow-Headers""Content-Type,...."); 
  8.  res.header("Access-Control-Allow-Methods""GET,..."); */ 
  9.  
  10.  // 試探請求:在CORS跨域請求中,首先瀏覽器會自己發送一個試探請求,驗證是否可以和服務器跨域通信,服務器返回200,則瀏覽器繼續發送真實的請求 
  11.  req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next(); 
  12. }); 
  13.  
  14. // 客戶端 
  15. let xhr = new XMLHttpRequest; 
  16. xhr.open('get''http://127.0.0.1:1001/list'); 
  17. xhr.setRequestHeader('Cookie''name=jason'); 
  18. xhr.withCredentials = true
  19. xhr.onreadystatechange = () => { 
  20.   if (xhr.status === 200 && xhr.readyState === 4) { 
  21.     console.log(xhr.responseText); 
  22.   } 
  23. }; 
  24. xhr.send(); 

當我們一旦在服務器端設置了允許任何源可以請求之后,其實請求是不安全的,并且要求客戶端不能攜帶資源憑證(比如上文中的Cookie字段),瀏覽器端會報錯。

告訴我們Cookie字段是不安全的也不能被設置的,如果允許源為'*'的話也是不允許的。

假如在我們的真實項目開發中

正確寫法✅

設置單一源(安全/也可以攜帶資源憑證/只能是單一一個源)
也可以動態設置多個源:每一次請求都會走這個中間件,我們首先設置一個白名單,如果當前客戶端請求的源在白名單中,我們把Allow-Origin動態設置為當前這個源

  1. app.use((req, res, next) => { 
  2.    
  3.   // 也可自定義白名單,檢驗請求的源是否在白名單里,動態設置 
  4.   /* let safeList = [ 
  5.   "http://127.0.0.1:5500"
  6.   xxx, 
  7.   xxxxx, 
  8.  ]; */ 
  9.  res.header("Access-Control-Allow-Origin""http://127.0.0.1:5500"); 
  10.  res.header("Access-Control-Allow-Credentials"true); // 設置是否可攜帶資源憑證 
  11.  
  12.  /* res.header("Access-Control-Allow-Headers""Content-Type,...."); 
  13.  res.header("Access-Control-Allow-Methods""GET,..."); */ 
  14.  
  15.  // 試探請求:在CORS跨域請求中,首先瀏覽器會自己發送一個試探請求,驗證是否可以和服務器跨域通信,服務器返回200,則瀏覽器繼續發送真實的請求 
  16.  req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next(); 
  17. }); 

CORS的好處
原理簡單,容易配置,允許攜帶資源憑證
仍可以用 ajax作為資源請求的方式
可以動態設置多個源,通過判斷,將Allow-Origin設置為當前源
CORS的局限性
只允許某一個源發起請求
如多個源,還需要動態判斷
Proxy
Proxy翻譯為“代理”,是由webpack配置的一個插件,叫"webpack-dev-server"(只能在開發環境中使用)

Proxy在webpack中的配置

  1. const path = require('path'); 
  2. const HtmlWebpackPlugin = require('html-webpack-plugin'); 
  3.  
  4. module.exports = { 
  5.   mode: 'production'
  6.   entry: './src/main.js'
  7.   output: {...}, 
  8.   devServer: { 
  9.     port: '3000'
  10.     compress: true
  11.     opentrue
  12.     hot: true
  13.     proxy: { 
  14.       '/': { 
  15.         target: 'http://127.0.0.1:3001'
  16.         changeOrigin: true 
  17.       } 
  18.     } 
  19.   }, 
  20.   // 配置WEBPACK的插件 
  21.   plugins: [ 
  22.     new HtmlWebpackPlugin({ 
  23.       template: `./public/index.html`, 
  24.       filename: `index.html` 
  25.     }) 
  26.   ] 
  27. }; 

圖解Proxy的原理

Proxy代理其實相當于由webpack-dev-server配置在本地創建了一個port=3000的服務,利用node的中間層代理(分發)解決了瀏覽器的同源策略限制。

但是它只能在開發環境下使用,因為dev-server只是一個webpack的一個插件;

如果需要在生產環境下使用,需要我們配置Nginx反向代理服務器;

另外如果是自己實現node服務層代理:無論是開發環境還是生產環境都可以處理(node中間層和客戶端是同源,中間層幫助我們向服務器請求數據,再把數據返回給客戶端)

Proxy的局限性
只能在本地開發階段使用

配置Nginx反向代理
主要作為生產環境下跨域的解決方案。

原理:利用Node中間層的分發機制,將請求的URL轉向服務器端的地址

配置反向代理

  1. server { 
  2.  listen: 80; 
  3.   server_name: 192.168.161.189; 
  4.   loaction: { 
  5.   proxy_pass_http://127.0.0.1:1001; // 請求轉向這個URL地址,服務器地址 
  6.     root html; 
  7.     index index.html; 
  8.   } 

簡單寫了一下偽代碼,實際開發中根據需求來配。

POST MESSAGE
假設現在有兩個頁面,分別為A頁面port=1001、B頁面port=1002,實現頁面A與頁面B的頁面通信(跨域)

原理:

把 B頁面當做A的子頁面嵌入到A頁面里,通過iframe.contentWindow.postMessage向B頁面傳遞某些信息
在A頁面中通過window.onmessage獲取A頁面傳遞過來的信息ev.data(見下代碼)
同理在B頁面中通過ev.source.postMessage向A頁面傳遞信息
在A頁面中通過window.onmessage獲取B頁面傳遞的信息
主要利用內置的postMessage和onmessage傳遞信息和接收信息。

A.html

  1. // 把 B頁面當做A的子頁面嵌入到A頁面里 
  2. <iframe id="iframe" src="http://127.0.0.1:1002/B.html" frameborder="0" style="display: none;"></iframe> 
  3.  
  4. <script> 
  5.   iframe.onload = function () { 
  6.     iframe.contentWindow.postMessage('珠峰培訓''http://127.0.0.1:1002/'); 
  7.   } 
  8.  
  9.   //=>監聽B傳遞的信息 
  10.   window.onmessage = function (ev) { 
  11.     console.log(ev.data); 
  12.   } 
  13. </script> 

B.html

  1. <script> 
  2.   //=>監聽A發送過來的信息 
  3.   window.onmessage = function (ev) { 
  4.     // console.log(ev.data); 
  5.  
  6.     //=>ev.source:A 
  7.     ev.source.postMessage(ev.data + '@@@''*'); 
  8.   } 
  9. </script> 

幾種方案的比較
1. JSONP方案需要前后端共同配置完成(利用script標簽不存在域的限制)【麻煩,老項目使用】

2. CORS原理簡單,但只能配置單一源,如果需要配置多個源,也只能從白名單中篩選出某一個符合表求的origin【偶爾使用】

服務器端需要單獨做處理,客戶端較為簡單

3. Proxy客戶端通過dev-server,生產環境需要配置Nginx反向代理(利用Node中間層分發機制)【常用】

 

 

責任編輯:姜華 來源: 前端時光屋
相關推薦

2012-03-27 15:23:15

JSONPAJAX

2023-05-06 15:32:04

2010-07-26 12:57:12

OPhone游戲開發

2011-07-04 10:39:57

Web

2021-03-16 08:54:35

AQSAbstractQueJava

2018-12-12 15:50:13

2010-07-26 13:55:10

OPhone游戲開發

2018-03-15 09:13:43

MySQL存儲引擎

2022-09-26 09:01:15

語言數據JavaScript

2009-11-30 16:46:29

學習Linux

2019-11-11 14:51:19

Java數據結構Properties

2022-12-02 09:13:28

SeataAT模式

2019-01-07 15:29:07

HadoopYarn架構調度器

2017-07-02 18:04:53

塊加密算法AES算法

2012-05-21 10:06:26

FrameworkCocoa

2021-07-20 15:20:02

FlatBuffers阿里云Java

2019-11-14 09:53:30

Set集合存儲

2009-12-25 15:49:43

Linux rescu

2023-12-04 13:22:00

JavaScript異步編程

2016-10-14 14:32:58

JavascriptDOMWeb
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99久久精品免费 | 超碰520 | 国产精品1 | 狠狠色综合欧美激情 | 无毛av| 久久激情视频 | 97精品超碰一区二区三区 | 精品一区二区三区视频在线观看 | 日操操夜操操 | 99爱在线 | 国产成人免费视频网站视频社区 | 日韩av资源站 | 综合一区二区三区 | 美女福利视频一区 | 日日摸夜夜添夜夜添特色大片 | 一级黄色片在线免费观看 | 精品伊人 | 97超碰成人| 九九在线| 日本精品一区二区三区四区 | 成人一区二区视频 | 青青草在线视频免费观看 | 欧美精品成人 | 黄色片免费看视频 | 美女天堂| 亚洲综合日韩精品欧美综合区 | 爱爱综合网 | 亚洲成av人片在线观看 | 亚洲视频免费观看 | 在线观看成年人视频 | 一区二区三区中文字幕 | 国产综合精品一区二区三区 | 欧美一区成人 | 狠狠躁躁夜夜躁波多野结依 | 999久久久久久久久6666 | 久久精品欧美视频 | 韩日精品视频 | 亚洲人免费视频 | 精产国产伦理一二三区 | 久久久久国产一区二区三区四区 | 国产传媒视频在线观看 |