跨域請求中常見的幾個問題

guoew發表於2019-06-06

一. Header相關的幾個概念

CORS: 跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP頭來告訴瀏覽器  讓執行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求。

出於安全原因,瀏覽器限制從指令碼內發起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味著使用這些API的Web應用程式只能從載入應用程式的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。

origin: web的origin 被定義為由協議,域和埠組成的 URL訪問。僅當協議,域和埠全部匹配,兩物件才具有相同的origin。

Access-Control-Allow-Origin: 該響應是否可以與來自給定的請求程式碼共享origin。  

Access-Control-Allow-Origin:*  #允許任何源的程式碼訪問資源
Access-Control-Allow-Origin: https://developer.mozilla.org  #允許請求來自https://developer.mozilla.org 的程式碼訪問資源。

Access-Control-Allow-Methods: 在對preflight request(預檢請求)的應答中明確了客戶端所要訪問的資源允許使用的方法或方法列表

Access-Control-Allow-Methods POST,GET,OPTIONS,PUT

Access-Control-Allow-Headers:  用於preflight request(預檢請求)種,列出了將會在正式請求的Access-Control-Expose-Headers 欄位中出現的首部資訊,用於響應包含在Access-Control-Request-Headers 首部的預檢請求。

Access-Control-Allow-Headers Content-Type,access-control-allow-credentials,access-control-allow-origin

Access-Control-Max-Age: 這個響應首部表示preflight request(預檢請求)的返回結果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的資訊) 可以被快取多久。

Access-Control-Max-Age: 600 #

CORS中的預檢請求(option): 在 CORS 中,可以使用 OPTIONS 方法發起一個預檢請求(一般都是瀏覽檢測到請求跨域時,會自動發起),以檢測實際請求是否可以被伺服器所接受。預檢請求報文中的 Access-Control-Request-Method首部欄位告知伺服器實際請求所使用的 HTTP 方法;Access-Control-Request-Headers 首部欄位告知伺服器實際請求所攜帶的自定義首部欄位。伺服器基於從預檢請求獲得的資訊來判斷,是否接受接下來的實際請求。

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

伺服器所返回的 Access-Control-Allow-Methods 首部欄位將所有允許的請求方法告知客戶端。該首部欄位與 Allow 類似,但只能用於涉及到 CORS 的場景中。

想要了解更多的http header資訊 請訪問 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers

二. 跨域常見問題排查(CORS場景)

2.1. 未配置 Access-Control-Allow-Origin。

Access to XMLHttpRequest at 'http://foo1.example.com/' from origin 'http://foo2.example.com/' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

解決方法: 在域為foo1.example.com 的虛擬主機新增 Access-Control-Allow-Origin 值為http://foo2.example.com。

server {
    ...
    server_name foo1.example.com;
    ...
    add_header 'Access-Control-Allow-Origin' 'http://foo2.example.com';
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';

    if ($request_method = "OPTIONS")  {
        return 204;
    }
    ...
}

2.2. 跨域訪問

Access to XMLHttpRequest at 'http://foo1.example.com/' from origin 'http://foo3.example.com/' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://foo2.example.com/' that is not equal to the supplied origin.

 解決方法:請注意觀察上述報錯, Access-Control-Allow-Origin 已經至少有一個值為http://foo2.example.com 的設定。所以現在配置一個允許多源訪問的配置。

server {
    ...
    server_name foo1.example.com;
    ...
    if ( $http_origin ~ .*.(example|aldwx).(net|com)) {
        set $other_domain $http_origin;
    }

    add_header Access-Control-Allow-Origin $other_domain;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';

    if ($request_method = "OPTIONS")  {
        return 204;
    }
    ...
}

2.3.  Access-Control-Allow-Headers 中首部缺失

Access to XMLHttpRequest at 'http://foo1.example.com/' from origin 'http://foo2.example.com/' has been blocked by CORS policy: Request header field <u>cookies</u> is not allowed by Access-Control-Allow-Headers in preflight response.

解決方法: 遇到這一類的問題時,需要仔細閱讀報錯,在報錯裡面基本已經寫明瞭答案。例如上面的這個報錯資訊。域foo2.example.com 請求域foo1.example.com 時,因首部cookies 未包含在 Access-Control-Allow-Headers 中,所有foo1無法響應客戶端請求。並且這一類問題可能同時會出現幾個首部缺失的請求,但是報錯是單個出現,所以呢,要仔細閱讀錯誤。下面也是其中一個報錯

Access to XMLHttpRequest at 'http://foo1.example.com/' from origin 'http://foo2.example.com/' has been blocked by CORS policy: Request header field <u>access-control-allow-credentials</u> is not allowed by Access-Control-Allow-Headers in preflight response.
server {
    ...
    server_name foo1.example.com;
    ...
     
    if ( $http_origin ~ .*.(example|aldwx).(net|com)) {
        set $other_domain $http_origin;
    }

    add_header Access-Control-Allow-Origin $other_domain;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'access-control-allow-credentials,cookies,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = "OPTIONS")  {
        return 204;
    }
    ...
}

參考文章:
Nginx配置跨域請求 Access-Control-Allow-Origin *

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers

相關文章