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-urlencoded
、multipart/form-data
、text/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
比如阿里雲中,
- 在左側儲存空間列表中,單擊目標儲存空間名稱,開啟該儲存空間概覽頁面。
- 單擊基礎設定頁籤,找到跨域設定區域,然後單擊設定。
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非簡單請求會發送兩次,第一次預檢 |
參考: