前端對Cross-Origin Resource Sharing 問題(CORS,中文又稱'跨域')應該很熟悉了。衆所周知出於安全的考慮,瀏覽器有個同源策略
,對於不一樣源的站點之間的相互請求會作限制(跨域限制是瀏覽器行爲,不是服務器行爲。)。不過下午想到了一個略無趣的問題:瀏覽器和服務器究竟是如何斷定有沒有跨域呢?本文主要分兩個部分,一是對這個問題的總結,二是nginx下如何配置服務器容許跨域。
<!-- more -->javascript
同源指的是域名(或IP),協議,端口都相同,不一樣源的客戶端腳本(javascript、ActionScript)在沒明確受權的狀況下,不能讀寫對方的資源。html
同源的斷定:
以http://www.example.com/dir/page.html
爲例,如下表格指出了不一樣形式的連接是否與其同源:(緣由裏未申明不一樣的屬性即說明其與例子裏的原連接對應的屬性相同)前端
連接 | 結果 | 緣由 |
---|---|---|
http:// www.example.com /dir/page2.html |
是 |
同協議同域名同端口 |
http:// www.example.com /dir2/other.html |
是 |
同協議同域名同端口 |
http://user:pwd@ www.example.com /dir2/other.html |
是 |
同協議同域名同端口 |
http://www.example.com: 81 /dir/other.html |
否 | 端口不一樣 |
https ://www.example.com/dir/other.html |
否 | 協議不一樣端口不一樣 |
http:// en.example.com /dir/other.html |
否 | 域名不一樣 |
http:// example.com /dir/other.html |
否 | 域名不一樣(要求精確匹配) |
http:// v2.www.example.com /dir/other.html |
否 | 域名不一樣(要求精確匹配) |
http://www.example.com: 80 /dir/other.html |
不肯定 |
取決於瀏覽器的實現方式 |
前文提到了同源策略的斷定,然而同源策略在增強了安全的同時,對開發倒是極大的不便利。所以開發者們又發明了不少辦法來容許數據的跨域傳輸(常見的辦法有JSONP
、CORS
)。當域名不一樣源的時候,因爲跨域實現的存在,瀏覽器不能直接根據域名來斷定跨域限制。那麼瀏覽器具體又是如何實現斷定的呢?看下面的例子。java
參與實驗的前端域名三個有:http://www.zhihu.com
、http://segmentfault.com
、http://localhost
。nginx
請求的服務器端地址爲http://localhost/city.json
,服務器解析引擎使用的nginx
,且服務器只配置了容許來自http://segmentfault.com
的跨域請求ajax
檢測方法:在各個域名下利用chrome瀏覽器的console
界面模擬發送ajax請求,代碼以下:chrome
在http://localhost
域名下,請求成功。json
服務器迴應的http文件頭以下:
在http://segmentfault.com
域名下,請求成功segmentfault
服務器迴應的http文件頭以下:
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 05 Jul 2015 18:17:27 GMT
Content-Type: application/octet-stream
Content-Length: 2084
Last-Modified: Sat, 18 Apr 2015 06:20:12 GMT
Connection: keep-alive
ETag: "5531f79c-824"
**Access-Control-Allow-origin: http://segmentfault.com**
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Accept-Ranges: bytes
在http://www.zhihu.com
下,請求失敗
雖然都是失敗,可是返回的HTTP文件頭內容會視服務器是否有配置跨域請求而發生變化跨域
(僅容許來自http://segmentfault.com
的跨域請求)
console.log窗口提示:
XMLHttpRequest cannot load http://localhost/city.json. The 'Access-Control-Allow-Origin' header has a value 'http://segmentfault.com' that is not equal to the supplied origin. Origin 'http://www.zhihu.com' is therefore notallowed access.
服務器迴應的http文件頭以下:
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 05 Jul 2015 17:59:25 GMT
Content-Type: application/octet-stream
Content-Length: 2084
Last-Modified: Sat, 18 Apr 2015 06:20:12 GMT
Connection: keep-alive
ETag: "5531f79c-824"
Access-Control-Allow-origin: http://segmentfault.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Accept-Ranges: bytes
console.log窗口提示:
XMLHttpRequest cannot load http://localhost/city.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.zhihu.com' is therefore not allowed access.
服務器迴應的http文件頭以下:
從zhihu
頁面的兩次瀏覽器報錯以及segmentfault
的成功返回值來看,能夠很容易得出瀏覽器和服務器的合做斷定步驟以下:
瀏覽器先根據同源策略對前端頁面和後臺交互地址作匹配,若同源,則直接發送數據請求;若不一樣源,則發送跨域請求。
服務器解析程序收到瀏覽器跨域請求後,根據自身配置返回對應文件頭。若未配置過任何容許跨域,則文件頭裏不包含
Access-Control-Allow-origin
字段,若配置過域名,則返回Access-Control-Allow-origin
+對應配置規則裏的域名的方式
。瀏覽器根據接受到的http文件頭裏的
Access-Control-Allow-origin
字段作匹配,若無該字段,說明不容許跨域;如有該字段,則對字段內容和當前域名作比對,若是同源,則說明能夠跨域,瀏覽器發送該請求;若不一樣源,則說明該域名不可跨域,不發送請求
(可是不能僅僅根據服務器返回的文件頭裏是否包含Access-Control-Allow-origin
來判斷其是否容許跨域,由於服務器端配置多域名跨域的時候,也會出現不能跨域的域名返回包裏沒有Access-Control-Allow-origin
字段的狀況。下文配置說明裏會講。)
前面講到了同源策略的基本斷定,以及瀏覽器實現跨域判斷的方式,那麼,如何在服務器端作配置來容許跨域傳輸呢?下文將以Nginx爲例,講一下三種狀況下的配置。
CORS經常使用的配置項有如下幾個:
Access-Control-Allow-Origin(必含) – 容許的域名,只能填通配符或者單域名
Access-Control-Allow-Methods(必含) – 這容許跨域請求的http方法(常見有POST
、GET
、OPTIONS
)
Access-Control-Allow-Headers(當預請求中包含Access-Control-Request-Headers時必須包含) – 這是對預請求當中Access-Control-Request-Headers的回覆,和上面同樣是以逗號分隔的列表,能夠返回全部支持的頭部。
Access-Control-Allow-Credentials(可選) – 該項標誌着請求當中是否包含cookies信息,只有一個可選值:true(必爲小寫)。若是不包含cookies,請略去該項,而不是填寫false。這一項與XmlHttpRequest2對象當中的withCredentials屬性應保持一致,即withCredentials爲true時該項也爲true;withCredentials爲false時,省略該項不寫。反之則致使請求失敗。
Access-Control-Max-Age(可選) – 以秒爲單位的緩存時間。預請求的的發送並不是免費午飯,容許時應當儘量緩存。
這個最省心
打開Nginx的配置文件(默認爲nginx.conf
)。找到對應域名設置的local
配置部分。
添加如下內容:
添加的域名必須帶
http://
協議頭(不然服務器沒法區分是http仍是https),若是接受全部域名的跨域請求,則能夠用*
(安全性有問題,不推薦)
若是容許跨域的域名有多個但出於安全問題又不想配置全域名通配的時候,就能夠用到nginx裏的if
判斷了。
添加以下內容:
若是對正則比較熟悉的,能夠直接用正則來匹配條件判斷,不須要用if這麼麻煩的方式。
'Access-Control-Allow-Methods' 容許多參數,'Access-Control-Allow-origin'不容許多參數,因此只能是條件語句判斷要不要加這個。這也是我前面提到的爲何即便HTTP文件頭返回值裏沒有'Access-Control-Allow-origin',也不能說明它就是不容許跨域的。
nginx配置文件的http
配置部分不能用if
條件語句,因此多域名的時候必須加在local
部份內。另外加在local
內的只對對應的服務器域名作跨域請求的配置,加在http
裏會讓跑在該nginx下的全部網站都統一採起這種配置。
Access-Control-Allow-Origin
也能夠改爲全小寫的形式,不影響結果.(access-control-allow-origin
也能夠)
參考連接:https://blog.csdn.net/zmx729618/article/details/53319383
https://www.cnblogs.com/shihaiming/p/9544060.html