1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文徹底搞懂跨域問題

        共 6857字,需瀏覽 14分鐘

         ·

        2022-06-29 19:31

        點擊上方 前端Q,關注公眾號

        回復加群,加入前端Q技術交流群


        什么是跨域

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

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

        比如:

        源 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]?



        同源策略是指,若頁面的源和頁面運行過程中加載的源不一致時,出于安全考慮,瀏覽器會對跨域的資源訪問進行一些限制

        image-20210916104747296

        同源策略對 ajax 的跨域限制的最為_兇狠_,默認情況下,它不允許 ajax 訪問跨域資源

        image-20210916105741041

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

        有多種方式解決跨域問題,常見的有:

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

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

        跨域解決方法1-代理

        對于前端開發(fā)而言,大部分的跨域問題,都是通過代理解決的

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

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

        image-20210916125008693

        在實際開發(fā)中,只需要對開發(fā)服務器稍加配置即可完成

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

        跨域解決方法2-JSONP

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

        要實現(xiàn)JSONP,需要瀏覽器和服務器來一個天衣無縫的絕妙配合。

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

        image-20210916151516184

        JSONP有著明顯的缺點,即其只能支持GET請求

        跨域解決方法3-CORS

        概述

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

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

        image-20200421152122793

        而要知道,一個請求可以附帶很多信息,從而會對服務器造成不同程度的影響

        比如有的請求只是獲取一些新聞,有的請求會改動服務器的數(shù)據(jù)

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

        • 簡單請求
        • 需要預檢的請求
        • 附帶身份憑證的請求

        這三種模式從上到下層層遞進,請求可以做的事越來越多,要求也越來越嚴格。

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

        簡單請求

        當瀏覽器端運行了一段 ajax 代碼(無論是使用 XMLHttpRequest 還是 fetch api),瀏覽器會首先判斷它屬于哪一種請求模式

        簡單請求的判定

        當請求同時滿足以下條件時,瀏覽器會認為它是一個簡單請求:

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

        如果以上三個條件同時滿足,瀏覽器判定為簡單請求。

        下面是一些例子:

        // 簡單請求
        fetch('http://crossdomain.com/api/news');

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

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

        // 簡單請求
        fetch('http://crossdomain.com/api/news', {
          method'post',
        });

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

        簡單請求的交互規(guī)范

        當瀏覽器判定某個ajax 跨域請求簡單請求時,會發(fā)生以下的事情

        1. 請求頭中會自動添加Origin字段

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

        // 簡單請求
        fetch('http://crossdomain.com/api/news');
        復制代碼

        請求發(fā)出后,請求頭會是下面的格式:

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

        看到最后一行沒,Origin字段會告訴服務器,是哪個源地址在跨域請求

        1. 服務器響應頭中應包含Access-Control-Allow-Origin

        當服務器收到請求后,如果允許該請求跨域訪問,需要在響應頭中添加Access-Control-Allow-Origin字段

        該字段的值可以是:

        • *:表示我很開放,什么人我都允許訪問
        • 具體的源:比如http://my.com,表示我就允許你訪問

        實際上,這兩個值對于客戶端http://my.com而言,都一樣,因為客戶端才不會管其他源服務器允不允許,就關心自己是否被允許

        當然,服務器也可以維護一個可被允許的源列表,如果請求的Origin命中該列表,才響應*或具體的源

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

        假設服務器做出了以下的響應:

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

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

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

        下圖簡述了整個交互過程

        image-20200421162846480

        需要預檢的請求

        簡單的請求對服務器的威脅不大,所以允許使用上述的簡單交互即可完成。

        但是,如果瀏覽器不認為這是一種簡單請求,就會按照下面的流程進行:

        1. 瀏覽器發(fā)送預檢請求,詢問服務器是否允許
        2. 服務器允許
        3. 瀏覽器發(fā)送真實請求
        4. 服務器完成真實的響應

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

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

        瀏覽器發(fā)現(xiàn)它不是一個簡單請求,則會按照下面的流程與服務器交互

        1. **瀏覽器發(fā)送預檢請求,詢問服務器是否允許**
        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ā)出的真實請求,請求中不包含我們的請求頭,也沒有消息體。

        這是一個預檢請求,它的目的是詢問服務器,是否允許后續(xù)的真實請求。

        預檢請求沒有請求體,它包含了后續(xù)真實請求要做的事情

        預檢請求有以下特征:

        • 請求方法為OPTIONS
        • 沒有請求體
        • 請求頭中包含
          • Origin:請求的源,和簡單請求的含義一致
          • Access-Control-Request-Method:后續(xù)的真實請求將使用的請求方法
          • Access-Control-Request-Headers:后續(xù)的真實請求會改動的請求頭
        1. 服務器允許

        服務器收到預檢請求后,可以檢查預檢請求中包含的信息,如果允許這樣的請求,需要響應下面的消息格式

        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
        ...
        復制代碼

        對于預檢請求,不需要響應任何的消息體,只需要在響應頭中添加:

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

        預檢被服務器允許后,瀏覽器就會發(fā)送真實請求了,上面的代碼會發(fā)生下面的請求數(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 }
        復制代碼
        1. **服務器響應真實請求**
        HTTP/1.1 200 OK
        Date: Tue, 21 Apr 2020 08:03:35 GMT
        ...
        Access-Control-Allow-Origin: http://my.com
        ...

        添加用戶成功
        復制代碼

        可以看出,當完成預檢之后,后續(xù)的處理與簡單請求相同

        下圖簡述了整個交互過程

        image-20200421165913320

        附帶身份憑證的請求

        默認情況下,ajax 的跨域請求并不會附帶 cookie,這樣一來,某些需要權限的操作就無法進行

        不過可以通過簡單的配置就可以實現(xiàn)附帶 cookie

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

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

        這樣一來,該跨域的 ajax 請求就是一個_附帶身份憑證的請求_

        當一個請求需要附帶 cookie 時,無論它是簡單請求,還是預檢請求,都會在請求頭中添加cookie字段

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

        告知的方式也非常的簡單,只需要在響應頭中添加:Access-Control-Allow-Credentials: true即可

        對于一個附帶身份憑證的請求,若服務器沒有明確告知,瀏覽器仍然視為跨域被拒絕。

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

        一個額外的補充

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

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

        Access-Control-Expose-Headers: authorization, a, b
        復制代碼

        這樣 JS 就能夠訪問指定的響應頭了。


        關于本文

        作者:一只前端小菜鳥

        https://juejin.cn/post/7094162429310926855

        往期推薦


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

        最后


        • 歡迎加我微信,拉你進技術群,長期交流學習...

        • 歡迎關注「前端Q」,認真學前端,做個專業(yè)的技術人...

        點個在看支持我吧

        瀏覽 41
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产美女一级真毛片酒店 | 男女交性| 国产精品久久久久久久久费观看 | 在线无码视频播放 | 又粗又硬又爽的免费视频 | 特级特黄AAAA免费看 | 色老板在线观看永久免费视频 | 动漫做爱网站 | 大胸美女裸胸 | 亚洲免费看黄 |