<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文徹底搞懂跨域問(wèn)題

          共 6857字,需瀏覽 14分鐘

           ·

          2022-06-29 19:31

          點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)

          回復(fù)加群,加入前端Q技術(shù)交流群


          什么是跨域

          瀏覽器有一個(gè)重要的安全策略,稱之為「同源策略」

          其中,源=協(xié)議+主機(jī)+端口源=協(xié)議+主機(jī)+端口源=協(xié)議+主機(jī)+端口,兩個(gè)源相同,稱之為同源,兩個(gè)源不同,稱之為跨源或跨域

          比如:

          源 1源 2是否同源
          www.baidu.com[1]www.baidu.com/news[2]?
          www.baidu.com[3]www.baidu.com[4]?
          http://localhost:5000[5]http://localhost:7000[6]?
          http://localhost:5000[7]http://127.0.0.1:5000[8]?
          www.baidu.com[9]baidu.com[10]?



          同源策略是指,若頁(yè)面的源和頁(yè)面運(yùn)行過(guò)程中加載的源不一致時(shí),出于安全考慮,瀏覽器會(huì)對(duì)跨域的資源訪問(wèn)進(jìn)行一些限制

          image-20210916104747296

          同源策略對(duì) ajax 的跨域限制的最為_(kāi)兇狠_,默認(rèn)情況下,它不允許 ajax 訪問(wèn)跨域資源

          image-20210916105741041

          所以,我們通常所說(shuō)的跨域問(wèn)題,就是同源策略對(duì) ajax 產(chǎn)生的影響

          有多種方式解決跨域問(wèn)題,常見(jiàn)的有:

          • 代理,常用
          • CORS,常用
          • JSONP

          無(wú)論使用哪一種方式,都是要讓瀏覽器知道,我這次跨域請(qǐng)求的是自己人,就不要攔截了。

          跨域解決方法1-代理

          對(duì)于前端開(kāi)發(fā)而言,大部分的跨域問(wèn)題,都是通過(guò)代理解決的

          代理適用的場(chǎng)景是:生產(chǎn)環(huán)境不發(fā)生跨域,但開(kāi)發(fā)環(huán)境發(fā)生跨域

          因此,只需要在開(kāi)發(fā)環(huán)境使用代理解決跨域即可,這種代理又稱之為開(kāi)發(fā)代理

          image-20210916125008693

          在實(shí)際開(kāi)發(fā)中,只需要對(duì)開(kāi)發(fā)服務(wù)器稍加配置即可完成

          // vue 的開(kāi)發(fā)服務(wù)器代理配置
          // vue.config.js
          module.exports = {
            devServer: { // 配置開(kāi)發(fā)服務(wù)器
              proxy: { // 配置代理
                "/api": { // 若請(qǐng)求路徑以 /api 開(kāi)頭
                  target"http://dev.taobao.com"// 將其轉(zhuǎn)發(fā)到 http://dev.taobao.com
                },
              },
            },
          };
          復(fù)制代碼

          跨域解決方法2-JSONP

          在CORS出現(xiàn)之前,人們想了一種奇妙的辦法來(lái)實(shí)現(xiàn)跨域,這就是JSONP。

          要實(shí)現(xiàn)JSONP,需要瀏覽器和服務(wù)器來(lái)一個(gè)天衣無(wú)縫的絕妙配合。

          JSONP的做法是:當(dāng)需要跨域請(qǐng)求時(shí),不使用AJAX,轉(zhuǎn)而生成一個(gè)script元素去請(qǐng)求服務(wù)器,由于瀏覽器并不阻止script元素的請(qǐng)求,這樣請(qǐng)求可以到達(dá)服務(wù)器。服務(wù)器拿到請(qǐng)求后,響應(yīng)一段JS代碼,這段代碼實(shí)際上是一個(gè)函數(shù)調(diào)用,調(diào)用的是客戶端預(yù)先生成好的函數(shù),并把瀏覽器需要的數(shù)據(jù)作為參數(shù)傳遞到函數(shù)中,從而間接的把數(shù)據(jù)傳遞給客戶端

          image-20210916151516184

          JSONP有著明顯的缺點(diǎn),即其只能支持GET請(qǐng)求

          跨域解決方法3-CORS

          概述

          CORS是基于http1.1的一種跨域解決方案,它的全稱是Cross-Origin Resource Sharing,跨域資源共享。

          它的總體思路是:如果瀏覽器要跨域訪問(wèn)服務(wù)器的資源,需要獲得服務(wù)器的允許

          image-20200421152122793

          而要知道,一個(gè)請(qǐng)求可以附帶很多信息,從而會(huì)對(duì)服務(wù)器造成不同程度的影響

          比如有的請(qǐng)求只是獲取一些新聞,有的請(qǐng)求會(huì)改動(dòng)服務(wù)器的數(shù)據(jù)

          針對(duì)不同的請(qǐng)求,CORS 規(guī)定了三種不同的交互模式,分別是:

          • 簡(jiǎn)單請(qǐng)求
          • 需要預(yù)檢的請(qǐng)求
          • 附帶身份憑證的請(qǐng)求

          這三種模式從上到下層層遞進(jìn),請(qǐng)求可以做的事越來(lái)越多,要求也越來(lái)越嚴(yán)格。

          下面分別說(shuō)明三種請(qǐng)求模式的具體規(guī)范。

          簡(jiǎn)單請(qǐng)求

          當(dāng)瀏覽器端運(yùn)行了一段 ajax 代碼(無(wú)論是使用 XMLHttpRequest 還是 fetch api),瀏覽器會(huì)首先判斷它屬于哪一種請(qǐng)求模式

          簡(jiǎn)單請(qǐng)求的判定

          當(dāng)請(qǐng)求同時(shí)滿足以下條件時(shí),瀏覽器會(huì)認(rèn)為它是一個(gè)簡(jiǎn)單請(qǐng)求:

          1. 請(qǐng)求方法屬于下面的一種:
            • get
            • post
            • head
          2. 請(qǐng)求頭僅包含安全的字段,常見(jiàn)的安全字段如下:
            • Accept
            • Accept-Language
            • Content-Language
            • Content-Type
            • DPR
            • Downlink
            • Save-Data
            • Viewport-Width
            • Width
          3. 請(qǐng)求頭如果包含Content-Type,僅限下面的值之一:
            • text/plain
            • multipart/form-data
            • application/x-www-form-urlencoded

          如果以上三個(gè)條件同時(shí)滿足,瀏覽器判定為簡(jiǎn)單請(qǐng)求。

          下面是一些例子:

          // 簡(jiǎn)單請(qǐng)求
          fetch('http://crossdomain.com/api/news');

          // 請(qǐng)求方法不滿足要求,不是簡(jiǎn)單請(qǐng)求
          fetch('http://crossdomain.com/api/news', {
            method'PUT',
          });

          // 加入了額外的請(qǐng)求頭,不是簡(jiǎn)單請(qǐng)求
          fetch('http://crossdomain.com/api/news', {
            headers: {
              a1,
            },
          });

          // 簡(jiǎn)單請(qǐng)求
          fetch('http://crossdomain.com/api/news', {
            method'post',
          });

          // content-type不滿足要求,不是簡(jiǎn)單請(qǐng)求
          fetch('http://crossdomain.com/api/news', {
            method'post',
            headers: {
              'content-type''application/json',
            },
          });
          復(fù)制代碼

          簡(jiǎn)單請(qǐng)求的交互規(guī)范

          當(dāng)瀏覽器判定某個(gè)ajax 跨域請(qǐng)求簡(jiǎn)單請(qǐng)求時(shí),會(huì)發(fā)生以下的事情

          1. 請(qǐng)求頭中會(huì)自動(dòng)添加Origin字段

          比如,在頁(yè)面http://my.com/index.html中有以下代碼造成了跨域

          // 簡(jiǎn)單請(qǐng)求
          fetch('http://crossdomain.com/api/news');
          復(fù)制代碼

          請(qǐng)求發(fā)出后,請(qǐng)求頭會(huì)是下面的格式:

          GET /api/news/ HTTP/1.1
          Host: crossdomain.com
          Connection: keep-alive
          ...
          Referer: http://my.com/index.html
          Origin: http://my.com
          復(fù)制代碼

          看到最后一行沒(méi),Origin字段會(huì)告訴服務(wù)器,是哪個(gè)源地址在跨域請(qǐng)求

          1. 服務(wù)器響應(yīng)頭中應(yīng)包含Access-Control-Allow-Origin

          當(dāng)服務(wù)器收到請(qǐng)求后,如果允許該請(qǐng)求跨域訪問(wèn),需要在響應(yīng)頭中添加Access-Control-Allow-Origin字段

          該字段的值可以是:

          • *:表示我很開(kāi)放,什么人我都允許訪問(wèn)
          • 具體的源:比如http://my.com,表示我就允許你訪問(wèn)

          實(shí)際上,這兩個(gè)值對(duì)于客戶端http://my.com而言,都一樣,因?yàn)榭蛻舳瞬挪粫?huì)管其他源服務(wù)器允不允許,就關(guān)心自己是否被允許

          當(dāng)然,服務(wù)器也可以維護(hù)一個(gè)可被允許的源列表,如果請(qǐng)求的Origin命中該列表,才響應(yīng)*或具體的源

          為了避免后續(xù)的麻煩,強(qiáng)烈推薦響應(yīng)具體的源

          假設(shè)服務(wù)器做出了以下的響應(yīng):

          HTTP/1.1 200 OK
          Date: Tue, 21 Apr 2020 08:03:35 GMT
          ...
          Access-Control-Allow-Origin: http://my.com
          ...

          消息體中的數(shù)據(jù)
          復(fù)制代碼

          當(dāng)瀏覽器看到服務(wù)器允許自己訪問(wèn)后,高興的像一個(gè)兩百斤的孩子,于是,它就把響應(yīng)順利的交給 js,以完成后續(xù)的操作

          下圖簡(jiǎn)述了整個(gè)交互過(guò)程

          image-20200421162846480

          需要預(yù)檢的請(qǐng)求

          簡(jiǎn)單的請(qǐng)求對(duì)服務(wù)器的威脅不大,所以允許使用上述的簡(jiǎn)單交互即可完成。

          但是,如果瀏覽器不認(rèn)為這是一種簡(jiǎn)單請(qǐng)求,就會(huì)按照下面的流程進(jìn)行:

          1. 瀏覽器發(fā)送預(yù)檢請(qǐng)求,詢問(wèn)服務(wù)器是否允許
          2. 服務(wù)器允許
          3. 瀏覽器發(fā)送真實(shí)請(qǐng)求
          4. 服務(wù)器完成真實(shí)的響應(yīng)

          比如,在頁(yè)面http://my.com/index.html中有以下代碼造成了跨域

          // 需要預(yù)檢的請(qǐng)求
          fetch('http://crossdomain.com/api/user', {
            method'POST'// post 請(qǐng)求
            headers: {
              // 設(shè)置請(qǐng)求頭
              a1,
              b2,
              'content-type''application/json',
            },
            bodyJSON.stringify({ name'袁小進(jìn)'age18 }), // 設(shè)置請(qǐng)求體
          });
          復(fù)制代碼

          瀏覽器發(fā)現(xiàn)它不是一個(gè)簡(jiǎn)單請(qǐng)求,則會(huì)按照下面的流程與服務(wù)器交互

          1. **瀏覽器發(fā)送預(yù)檢請(qǐng)求,詢問(wèn)服務(wù)器是否允許**
          OPTIONS /api/user HTTP/1.1
          Host: crossdomain.com
          ...
          Origin: http://my.com
          Access-Control-Request-Method: POST
          Access-Control-Request-Headers: a, b, content-type
          復(fù)制代碼

          可以看出,這并非我們想要發(fā)出的真實(shí)請(qǐng)求,請(qǐng)求中不包含我們的請(qǐng)求頭,也沒(méi)有消息體。

          這是一個(gè)預(yù)檢請(qǐng)求,它的目的是詢問(wèn)服務(wù)器,是否允許后續(xù)的真實(shí)請(qǐng)求。

          預(yù)檢請(qǐng)求沒(méi)有請(qǐng)求體,它包含了后續(xù)真實(shí)請(qǐng)求要做的事情

          預(yù)檢請(qǐng)求有以下特征:

          • 請(qǐng)求方法為OPTIONS
          • 沒(méi)有請(qǐng)求體
          • 請(qǐng)求頭中包含
            • Origin:請(qǐng)求的源,和簡(jiǎn)單請(qǐng)求的含義一致
            • Access-Control-Request-Method:后續(xù)的真實(shí)請(qǐng)求將使用的請(qǐng)求方法
            • Access-Control-Request-Headers:后續(xù)的真實(shí)請(qǐng)求會(huì)改動(dòng)的請(qǐng)求頭
          1. 服務(wù)器允許

          服務(wù)器收到預(yù)檢請(qǐng)求后,可以檢查預(yù)檢請(qǐng)求中包含的信息,如果允許這樣的請(qǐng)求,需要響應(yīng)下面的消息格式

          HTTP/1.1 200 OK
          Date: Tue, 21 Apr 2020 08:03:35 GMT
          ...
          Access-Control-Allow-Origin: http://my.com
          Access-Control-Allow-Methods: POST
          Access-Control-Allow-Headers: a, b, content-type
          Access-Control-Max-Age: 86400
          ...
          復(fù)制代碼

          對(duì)于預(yù)檢請(qǐng)求,不需要響應(yīng)任何的消息體,只需要在響應(yīng)頭中添加:

          • Access-Control-Allow-Origin:和簡(jiǎn)單請(qǐng)求一樣,表示允許的源
          • Access-Control-Allow-Methods:表示允許的后續(xù)真實(shí)的請(qǐng)求方法
          • Access-Control-Allow-Headers:表示允許改動(dòng)的請(qǐng)求頭
          • Access-Control-Max-Age:告訴瀏覽器,多少秒內(nèi),對(duì)于同樣的請(qǐng)求源、方法、頭,都不需要再發(fā)送預(yù)檢請(qǐng)求了
          1. 瀏覽器發(fā)送真實(shí)請(qǐng)求

          預(yù)檢被服務(wù)器允許后,瀏覽器就會(huì)發(fā)送真實(shí)請(qǐng)求了,上面的代碼會(huì)發(fā)生下面的請(qǐng)求數(shù)據(jù)

          POST /api/user HTTP/1.1
          Host: crossdomain.com
          Connection: keep-alive
          ...
          Referer: http://my.com/index.html
          Origin: http://my.com

          {"name""xiaoming""age": 18 }
          復(fù)制代碼
          1. **服務(wù)器響應(yīng)真實(shí)請(qǐng)求**
          HTTP/1.1 200 OK
          Date: Tue, 21 Apr 2020 08:03:35 GMT
          ...
          Access-Control-Allow-Origin: http://my.com
          ...

          添加用戶成功
          復(fù)制代碼

          可以看出,當(dāng)完成預(yù)檢之后,后續(xù)的處理與簡(jiǎn)單請(qǐng)求相同

          下圖簡(jiǎn)述了整個(gè)交互過(guò)程

          image-20200421165913320

          附帶身份憑證的請(qǐng)求

          默認(rèn)情況下,ajax 的跨域請(qǐng)求并不會(huì)附帶 cookie,這樣一來(lái),某些需要權(quán)限的操作就無(wú)法進(jìn)行

          不過(guò)可以通過(guò)簡(jiǎn)單的配置就可以實(shí)現(xiàn)附帶 cookie

          // xhr
          var xhr = new XMLHttpRequest();
          xhr.withCredentials = true;

          // fetch api
          fetch(url, {
            credentials'include',
          });
          復(fù)制代碼

          這樣一來(lái),該跨域的 ajax 請(qǐng)求就是一個(gè)_附帶身份憑證的請(qǐng)求_

          當(dāng)一個(gè)請(qǐng)求需要附帶 cookie 時(shí),無(wú)論它是簡(jiǎn)單請(qǐng)求,還是預(yù)檢請(qǐng)求,都會(huì)在請(qǐng)求頭中添加cookie字段

          而服務(wù)器響應(yīng)時(shí),需要明確告知客戶端:服務(wù)器允許這樣的憑據(jù)

          告知的方式也非常的簡(jiǎn)單,只需要在響應(yīng)頭中添加:Access-Control-Allow-Credentials: true即可

          對(duì)于一個(gè)附帶身份憑證的請(qǐng)求,若服務(wù)器沒(méi)有明確告知,瀏覽器仍然視為跨域被拒絕。

          另外要特別注意的是:**對(duì)于附帶身份憑證的請(qǐng)求,服務(wù)器不得設(shè)置 Access-Control-Allow-Origin 的值為***。這就是為什么不推薦使用*的原因

          一個(gè)額外的補(bǔ)充

          在跨域訪問(wèn)時(shí),JS 只能拿到一些最基本的響應(yīng)頭,如:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要訪問(wèn)其他頭,則需要服務(wù)器設(shè)置本響應(yīng)頭。

          Access-Control-Expose-Headers頭讓服務(wù)器把允許瀏覽器訪問(wèn)的頭放入白名單,例如:

          Access-Control-Expose-Headers: authorization, a, b
          復(fù)制代碼

          這樣 JS 就能夠訪問(wèn)指定的響應(yīng)頭了。


          關(guān)于本文

          作者:一只前端小菜鳥(niǎo)

          https://juejin.cn/post/7094162429310926855

          往期推薦


          從零到一建立一套完整的前端規(guī)范
          接了很多私活之后的感觸
          前端一個(gè)月面試小記,字節(jié)、螞蟻、美團(tuán)、滴滴

          最后


          • 歡迎加我微信,拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專業(yè)的技術(shù)人...

          點(diǎn)個(gè)在看支持我吧

          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国内毛片毛片毛片毛片毛片毛片毛片毛片毛片毛片毛片毛片 | 久久天天爽 | 97操逼网 | 色哟哟国产精品 | 成人AV电影网 久久爱 |