1. 程式人生 > >CORS ———— 跨域解決方案之二

CORS ———— 跨域解決方案之二

其他跨域方案請看:

以下介紹CORS跨域解決方案

一、什麼是CORS?

CORS (Corss-Orign Resource Sharing) 是W3C工作草案,是一份瀏覽器技術的規範。定義了跨域資源訪問時,瀏覽器和伺服器之間如何通訊,使用自定義的http頭部允許瀏覽器和伺服器相互瞭解對方,從而決定請求或響應成功與否。CORS在現代瀏覽器都支援,使用和普通的ajax沒有任何區別,關鍵是隻要伺服器實現CORS介面。

二、如何使用

現代瀏覽器都支援CORS,所以前端不需要做任何改變,即使普通的ajax,只需要伺服器實現CORS介面;http如何請求伺服器,我們不會有如何感覺。

三、服務端處理流程

HTTP請求

1、檢視http頭部是否有Origin,沒有直接結束,有到第2步。

2、檢視Origin是否有效,無效則返回403,有效則第3步。

3、檢視method是否有效,無效則返回403,是有效則第4步。

4、檢視是否是OPTION請求,不是則簡單請求處理,是則預檢處理。

簡單請求處理:

返回Allow-Origin, Allow-Credentials等,並返回正常內容

預檢處理:

返回Allow-Origin, Allow-Credentials等,內容為空,只有當瀏覽器下一次發出實際http請求才返回內容

四、客戶端處理機制

瀏覽器將CORS請求分成兩類:簡單請求和預檢請求(非簡單請求),不需要做其他設定

劃分的依據為:

(1) 簡單請求(需要同時滿足下面兩個條件)

         請求方法是:HEAD ,GET,POST 三者之一

         HTTP頭資訊不超過以下欄位:Accept,Accept-Language,Content-Language,Last-Event-ID,Content-Type(只限於三個值application/x-www-form-urlencodedmultipart/form-datatext/plain)

        簡單請求時,瀏覽器會直接發起一個跨域請求,並在請求頭中攜帶上Origin,用來說明本次請求來自哪個源。伺服器端接受到請求後,根據頭資訊來判斷是否允許跨域,返回一個正常的HTTP響應(如果允許,則響應頭攜帶Access-Control-Allow-Origin等欄位,並返回正常內容)。瀏覽器根據返回的響應頭是否攜帶Access-Control-Allow-Origin等欄位判斷請求是否請求成功,沒有則丟擲異常。注意:請求是否成功不能通過狀態碼來確定,因為有可能也是200,但是請求拒絕了。

       異常:

       正確:

(2) 非簡單請求

只要不能同時滿足簡單請求中任意一個條件的就是非簡單請求,如果預檢成功,瀏覽器實際一共會發送兩次請求。

分別:

option請求

預檢成功則傳送正常的post請求

五、伺服器端配置

伺服器端可以在多個地方設定,比如ngnix、oss、cdn、web伺服器等

主要涉及到的是:

Name Required Comments
Access-Control-Allow-Origin 必填 允許請求的域,比如:http://www.baidu.com或者所有都允許*
Access-Control-Allow-Methods 必填 允許請求的方法,比如:get、post、put、delete,多個用逗號分割,或者允許所有*
Access-Control-Allow-Headers 可選 預檢請求後,告知傳送請求需要有的頭部
Access-Control-Expose-Headers 可選

CORS請求時,xmlhttprequest預設只能拿到6個基本欄位:Cache-Control、

Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿

到其他欄位,就必須在Access-Control-Expose-Headers裡面指定。

Access-Control-Max-Age 可選 本次預檢的有效期,單位:秒;在有效期內不需要發出另一條預檢
Access-Control-Allow-Credentials 可選

表示是否允許傳送cookie,預設false;比如put或delete,濁者content-type為

application/json等的有特殊要求,需要設定為true

1、web伺服器

可以通過新增過濾器,或攔截器等處理

<filter>
	<display-name>CORSFilter</display-name>
	<filter-name>CORSFilter</filter-name>
	<filter-class>com.learn.mybatis.controller.filter.CORSFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>CORSFilter</filter-name>
	<url-pattern>/home</url-pattern>
</filter-mapping>
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse response2 = (HttpServletResponse) response;
    response2.setHeader("Access-Control-Allow-Origin", "*");
    response2.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
    response2.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization");
    response2.setHeader("Access-Control-Max-Age", "10");
	    
	chain.doFilter(request, response2);
}

2、ngnix

location / {
  if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*';
    #
    # Om nom nom cookies
    #
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    #
    # Custom headers and headers various browsers **should** be OK with but aren't
    #
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    #
    # Tell client that this pre-flight info is valid for 20 days
    #
    add_header 'Access-Control-Max-Age' 1728000;
    add_header 'Content-Type' 'text/plain charset=UTF-8';
    add_header 'Content-Length' 0;
    return 204;
  }
  if ($request_method = 'POST') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
  }
  if ($request_method = 'GET') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
  }
}

3、oss

比如阿里雲中,

  1. 在左側儲存空間列表中,單擊目標儲存空間名稱,開啟該儲存空間概覽頁面。
  2. 單擊基礎設定頁籤,找到跨域設定區域,然後單擊設定。

4、cdn

六、帶認證的請求

CORS預設情況下是不需要提供cookie,http認證,ssl等的,如果需要攜帶認證,則需要設定

xmlhttprequest.withCredentials = true;

伺服器端需要設定:

Access-Control-Allow-Credentials為true。

不然會報錯:

當伺服器設定帶認證,則Access-Control-Allow-Origin不能設定為*,必須為一個具體的域名。

不然也會報錯:

設定正確後,將攜帶上cookie:

七、錯誤處理

因為CORS錯誤,瀏覽器有可能返回的也是200,所以通過狀態碼無法判定是否出錯。

可以使用onerror監聽錯誤,使用onload檢測成功

xhr.onerror = function(){
    conlose.log("cors error");
}

八、弊端、缺陷

只要伺服器Access-Control-Allow-Origin不要設定成*,同時做好認證,在安全性上是比較可靠的,但是在低版本瀏覽器上並不支援,只能使用jsonp。

九、JSONP和CORS比較

Name Compare Usage
安全性

因為JSONP不是規範,所以存在很明顯的安全漏洞,比如callback注入等。

但是安全沒有絕對性,CORS也同樣存在漏洞。

相容性 JSONP相容型很強,CORS只支援現在瀏覽器,低版本ie等不支援 低版本使用JSONP,現在瀏覽器推薦CORS
請求方式 JSONP只能使用GET,CORS支援所有的http請求 有增刪改查,推薦使用CORS
錯誤處理 JSONP錯誤處理不完善,CORS可以監聽onerror,並通過瀏覽器控制檯檢視
複雜度 JSONP傳送一次get請求,CORS非簡單請求會發送兩次,第一次預檢

參考: