Nginx跨域問題

jiayou111發表於2024-07-02

目錄
  • Nginx跨域實現
  • 跨域場景
  • 跨域問題的解決方案
  • Nginx配置跨域解決

Nginx跨域實現

首先大家要搞清楚什麼是跨域,為什麼會有跨域情況的出現。哪些情況屬於跨域?
跨域:由於瀏覽器的同源策略,即屬於不同域的頁面之間不能相互訪問各自的頁面內容
注:同源策略,單說來就是同協議,同域名,同埠

URL 說明 是否允許通訊
http://www.a.com/a.js
http://www.a.com/b.js 同一域名下 允許

http://www.a.com/lab/a.js
http://www.a.com/script/b.js 同一域名下不同資料夾 允許

http://www.a.com:8000/a.js
http://www.a.com/b.js 同一域名,不同埠 不允許

http://www.a.com/a.js
https://www.a.com/b.js 同一域名,不同協議 不允許

http://www.a.com/a.js
http://70.32.92.74/b.js 域名和域名對應ip 不允許

http://www.a.com/a.js
http://script.a.com/b.js 主域相同,子域不同 不允許

http://www.a.com/a.js
http://a.com/b.js 同一域名,不同二級域名(同上) 不允許(cookie這種情況下也不允許訪問)

http://www.cnblogs.com/a.js
http://www.a.com/b.js 不同域名 不允許

跨域場景

出於安全考慮(比如csrf攻擊),瀏覽器一般會禁止進行跨域訪問,但是因為有時有相應需求,需要允許跨域訪問,這時,我們就需要將跨域訪問限制開啟。
啟動一個web服務,埠是8081。

然後再開啟一個web服務/前端服務都可以。埠是8082,然後再8082的服務中透過ajax來訪問8081的服務,這就不滿足同源策略,就會出現跨域問題。

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
<h2>Hello World!</h2>
<script type="text/javascript">
    function fun1(){
        var request = new XMLHttpRequest();
        request.open("GET","http://localhost:8081/user/query")
        request.send();
        request.onreadystatechange = function(){
            if(request.status==200 && request.readyState == 4){
                console.log("響應的結果" + request.responseText)
            }
        }
    }
</script>
</body>
    <input type="button" value="跨域呼叫" onclick="fun1()">
</html>

跨域問題的解決方案

解決跨域問題的方式也有多種:
1.前後端結合(JsonP)
雖然jsonp也可以實現跨域,但是因為jsonp不支援post請求,應用場景受到很大限制,所以這裡不對jsonp作介紹。

2.純後端方式一(CORS方式)
CORS是w3c標準的方式,透過在web伺服器端設定:響應頭Access-Cntrol-Alow-Origin來指定哪些域可以訪問本域的資料,ie8&9(XDomainRequest),10+,chrom4,firefox3.5,safair4,opera12支援這種方式。

伺服器代理,同源策略只存在瀏覽器端,透過伺服器轉發請求可以達到跨域請求的目的,劣勢:增加伺服器的負擔,且訪問速度慢。

3.純後端方式二(Nginx代理方式)

首先配置Nginx的反向代理方式

代理訪問正常

8082的服務訪問Nginx,出現了跨域問題

Nginx配置跨域解決

nginx配置如下:

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
}
proxy_pass http://192.168.12.1:8081;
} 

解決了跨域問題

引數說明
Access-Control-Allow-Origin
伺服器預設是不被允許跨域的。給Nginx伺服器配置Access-Control-Allow-Origin *後,表示伺服器可以接受所有的請求源(Origin),即接受所有跨域的請求。

Access-Control-Allow-Headers
是為了防止出現以下錯誤:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
這個錯誤表示當前請求Content-Type的值不被支援。其實是我們發起了"application/json"的型別請求導致的。這裡涉及到一個概念:預檢請求(preflight request)

Access-Control-Allow-Methods
是為了防止出現以下錯誤:
Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

給OPTIONS新增204的返回
是為了處理在傳送POST請求時Nginx依然拒絕訪問的錯誤,傳送"預檢請求"時,需要用到方法OPTIONS,所以伺服器需要允許該方法。

預檢請求(preflight request)
跨域資源共享(CORS)標準新增了一組 HTTP 首部欄位,允許伺服器宣告哪些源站有許可權訪問哪些資源。另外,規範要求,對那些可能對伺服器資料產生副作用的HTTP 請求方法(特別是GET以外的HTTP請求,或者搭配某些 MIME 型別的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。伺服器確認允許之後,才發起實際的 HTTP 請求。在預檢請求的返回中,伺服器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關資料)。
其實Content-Type欄位的型別為application/json的請求就是上面所說的搭配某些 MIME 型別的 POST 請求,CORS規定,Content-Type不屬於以下MIME型別的,都屬於預檢請求
所以 application/json的請求 會在正式通訊之前,增加一次"預檢"請求,這次"預檢"請求會帶上頭部資訊 Access-Control-Request-Headers: Content-Type:

OPTIONS /api/test HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
... 

伺服器回應時,返回的頭部資訊如果不包含Access-Control-Allow-Headers: Content-Type則表示不接受非預設的的Content-Type。
即出現以下錯誤:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

相關文章