一篇文章讓你搞懂如何通過 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
<%@ 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 支持這種方式。
服務器代理,同源策略只存在瀏覽器端,通過服務器轉發請求可以達到跨域請求的目的,劣勢:增加服務器的負擔,且訪問速度慢。
- 純後端方式二(Nginx 代理方式)【建議這種方式】
首先配置 Nginx 的反向代理方式
代理訪問正常
8082 的服務訪問 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.
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/FBl0NDo--no94_OEhWY5Jw