同源策略
同源策略是瀏覽器保護用戶安全上網的重要措施,協議、域名、端口號三者相同即為同源。
不同源下,瀏覽器不允許js操作Cookie、LocalStorage、DOM等數據或頁面元素,也不允許發送ajax請求,同源下則不受影響。
下圖是在Chrom控制台中發送ajax跨域請求的報錯信息:

圖片中黃色部分提示響應被阻止,說明在跨域的情況下,請求依然發送到了服務器且服務器返回了數據,只是被瀏覽器攔下了。
對於跨域問題可以使用CORS來解決,使用CORS時,HTTP請求分為兩種情況:簡單請求與復雜請求。
簡單請求
滿足以下三點即為簡單請求:
- HTTP請求方法為GET、POST或HEAD
- HTTP請求頭只能包含
Accept, Accept-Language, Content-Language, Content-Type或Last-Event-ID - ContentType的值只能為以下三種:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
復雜請求
除簡單請求之外即為復雜請求。瀏覽器在發送復雜請求前會先發送Preflight request(預檢請求),即發送OPTIONS請求。注意是瀏覽器發送的,用戶無感。
預檢請求頭包含兩個特定字段:
- Access-Control-Request-Method
表示后續請求會用到的HTTP方法,該字段必選 - Access-Control-Request-Headers
后續請求中所設置的請求頭部信息,注意,這里不包含瀏覽器默認設置的頭部字段,如:User-Agent。該字段可不發送。
服務器會檢查對預檢請求中的Origin、Access-Control-Request-Method、Access-Control-Request-Headers字段值,並返回正常的HTTP響應。
瀏覽器根據返回信息判斷后續請求是否符合服務器端跨域要求,不符合則拋出錯誤信息。通過預檢請求后,則發送后續請求,此時和簡單請求無差別。
服務器配置CORS的幾個字段
- Access-Control-Allow-Origin
必選,設置允許哪些源訪問服務器資源 - Access-Control-Allow-Methods
必選,設置允許哪些HTTP方法 - Access-Control-Request-Headers
設置HTTP請求頭中包含哪些字段,如果瀏覽器請求包括Access-Control-Request-Headers字段,則必選
以上三個字段為常用字段,其余字段配置參考:CORS policy options。
withCredentials與Cookie的跨域問題
Cookie受到同源策略的限制沒有那么嚴格,默認情況下,只要發送請求方所在域與Cookie的Domain值相同即可將cookie發送至服務器端,無需考慮協議和端口號。在默認情況下,客戶端發起的HTTP請求會帶上目標域的Cookie,但無法攜帶其它屬於其它的域Cookie。
我們可以借助XMLHttpRequest對象的withCredentials屬性及CORS的Access-Control-Allow-Credentials二者來實現跨域的Cookie發送和寫入。
var xhr=new XMLHttpRequest(); xhr.open('GET','http://www.target.com:8093/api/GetAllProductType'); xhr.onreadystatechange=function(){ if(xhr.readyState==4 && xhr.status==200){ console.log(xhr.response); } } // 這里使用withCredentials屬性來發送Cookie xhr.withCredentials=true; xhr.send();
注意,在使用withCredentials時,服務器端不能將Access-Control-Allow-Origin的值配置為*,否則客戶端會報錯:
Access to XMLHttpRequest at ''http://www.target.com:8093/api/GetAllProductType' from origin 'http://www.request.com:8094' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
一個問題
上周在ASP.NET Web API 2中使用CORS,報錯:The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed。原因是服務器端配置了兩次CORS,導致返回了兩個Access-Control-Allow-Origin:*但瀏覽器只允許一個。
經過排查發現在Web.config文件中也配置了CORS,與代碼中的配置重復,注釋掉之后問題解決。該問題參考了:stackoverflow上的回答。
小結
同源策略是瀏覽器為保障用戶(數據)安全而對JS功能進行一定限制。畢竟HTML與CSS只負責網頁結構與樣式,不具備操作頁面元素及與服務器交互的功能。
離開瀏覽器環境后跨域問題也就不復存在。
嚴格的限制會導致一些不便,故同源策略開了幾個口子:
-
Cookie共享
子域名可以共享父級域名的cookie -
嵌入式資源獲取
<script>,<img>,<link>等標簽獲取資源不受同源策略限制,這也是JSONP實現跨域的原理
常用處理跨域請求的方式有JSONP和CORS:
-
JSONP
需要前后端協作處理且只支持GET請求
不是標准規范
對老式瀏覽器友好(這里想到了老古董IE:) -
CORS
支持GET、POST、PUT、DELETE等多種請求
服務器端配置簡單且不需要前端寫額外的代碼
目前主流瀏覽器均支持CORS規范
推薦閱讀
