淺談CORS CORS全稱“跨站資源共用”(Cross Origin Resource Sharing),它允許瀏覽器剋服瀏覽器同源策略向跨域伺服器發出請求。 同源策略 概念 說到CORS,那麼就不得不提瀏覽器同源策略,所謂“同源”,是指伺服器URL的三個相同: 1.協議相同 2.功能變數名稱相同 3.埠 ...
淺談CORS
CORS全稱“跨站資源共用”(Cross-Origin Resource Sharing),它允許瀏覽器剋服瀏覽器同源策略向跨域伺服器發出請求。
同源策略
概念
說到CORS,那麼就不得不提瀏覽器同源策略,所謂“同源”,是指伺服器URL的三個相同:
1.協議相同
2.功能變數名稱相同
3.埠相同
舉個慄子:比如一個URL是http://www.example.com:80/a.html
,那麼:
http://www.example.com:80/b.html // 同源
https://www.example.com:80/a.html // 非同源(協議不同)
http://www.example1.com:80/a.html // 非同源(功能變數名稱不同)
http://www.example.com:81/a.html // 非同源(埠不同)
限制
如果非同源,那麼三種行為將受到限制:
1.非同源頁面無法跨域讀取瀏覽器本地數據存儲(Cookie、LocalStorage和IndexDB)
2.非同源頁面無法跨域獲取DOM
3.非同源頁面無法跨域發送AJAX請求
目的
那麼,為什麼瀏覽器要使用同源策略?
同源策略的目的,是為了保證用戶的信息安全,防止被不法分子竊取數據。而眾所周知,Cookie包含大量的登錄信息,如果一個網頁可以跨域訪問另一個網站的Cookie,那麼不法分子可以通過使用跨域訪問獲取Cookie然後冒充用戶,為所欲為。
由此可見,同源策略是極其有必要的。
突破同源策略
但是,很多時候,我們需要跨域發送AJAX請求,此時我們就需要突破同源策略不允許發送跨域AJAX的規定。隨著技術的發展,有很多技術可以實現跨域發送AJAX請求,常見的有以下三種:
1.JSONP
2.Websocket
3.CORS
JSONP
JSONP是CORS技術出來之前最常用的跨域解決方案,最大的特定是相容性好,簡單,不需要進行大的伺服器改動。它的基本思路是通過動態添加一個script標簽,向伺服器請求腳本,腳本中一般調用一個客戶端定義的函數,將數據作為參數,調用客戶端的函數,而客戶端通過操作該函數,可以使用被當做參數傳過來的數據。
因為伺服器不限制script的跨域,所以不受跨域影響。
Websocket
眾所周知,Websocket是一個持久化協議,常用於解決伺服器推送問題。但是,實際上Websocket其實支持跨域通信。通過設置Websocket的origin
的欄位,可以規定允許跨域的站點。
上面兩種方法雖然可以解決跨域,但是,都有著各種問題。
慶幸的是,本文的主角:CORS的出現,徹底解決了跨域問題。
CORS
瀏覽器將跨域AJAX請求分為兩類:簡單請求和非簡單請求,對應有兩種不同的處理方式。
簡單請求
何為簡單請求?
簡單請求就是滿足以下兩個條件的請求:
1.請求方法為HEAD、GET和POST
2.HTTP請求頭只包含:Accept
、Accept-Language
、Content-Language
、Last-Event-ID
以及值為application/x-www-form-urlencoded
、multipart/form-data
、text/plain
三者之一的Content-Type
對於簡單請求,瀏覽器可以直接發送請求到伺服器,但是會在請求頭中添加一個origin
欄位,該欄位用來說明請求的來源。伺服器會識別該欄位,判斷是否允許跨域。
如果允許跨域,伺服器會返回結果併在響應頭上添加三個欄位:
1.Access-Control-Allow-Origin
該欄位的值為Origin欄位的值,或者是*
,表示伺服器接受任何源的跨域請求。
2.Access-Control-Allow-Credentials
可選欄位,它表示是否允許發送Cookie,值為true
時,表示發送請求的時候允許發送Cookie,如果不包含該欄位,則表示不允許發送Cookie。
值得一提的是,如果伺服器允許發送Cookie,那麼不允許將Access-Control-Allow-Origin
的值設為*
。
3.Access-Control-Expose-Headers
可選欄位,在沒有該欄位的情況下,針對跨域請求,XHR對象的getResponseHeader()
方法只能拿到Cache-Control
、Content-Language
、Content-Type
、Expire
、Last-Modified
、Pragma
這六個欄位,該欄位可以設置額外可以拿到的欄位。
非簡單請求
不滿足簡單請求的跨域請求都是非簡單請求,比如PUT或DELETE方法。
不同於簡單請求的直接向伺服器請求,非簡單請求會在發送之前,先進行一次“預檢”(preflight),即,向伺服器發出一個OPTIONS請求,查詢伺服器是否允許它進行跨域請求。
如果伺服器不通過“預檢”,會返回一個error,客戶端可以通過onerror事件進行捕獲。
當伺服器通過“預檢”後,伺服器會進行響應,響應頭中含有CORS的相關欄位,分別是:
1.Access-Control-Allow-Origin
該欄位和簡單請求中的同名欄位一樣。
2.Access-Control-Allow-Methods
該欄位表示伺服器支持跨域的所有方法,是一個逗號分隔的字元串,如:POST,DELETE。
3.Access-Control-Allow-Headers
該欄位表示伺服器支持的所有頭信息,也是一個逗號分隔的字元串。
4.Access-Control-Allow-Credentials
可選欄位,與簡單請求中的同名欄位一樣。
5.Access-Control-Max-Age
可選欄位,在一段時間內,瀏覽器對同一個功能變數名稱進行非簡單跨域請求,只對第一次進行“預檢”,而這一次“預檢”的結果將被緩存,接下來的請求都通過該結果進行判斷。該欄位就是用來設置“預檢”結果緩存的時間長短,可以將其值設為-1
來禁用“預檢”緩存。
接收到伺服器通過“預檢”的響應後,客戶端會正式發送真正的請求,接下來的處理方式和簡單請求一致。
總結
在當前開發中,當不需要相容老式瀏覽器中,我們一般採用CORS的方式進行跨域請求,因為相比Websocket,CORS支持非長連接場景;相比JSONP,CORS支持所有HTTP請求,用法更加平滑。
當然,值得一提的是,當你需要相容老式瀏覽器時,JSONP是你唯一的選擇~