在我們日常的項目開發時使用AJAX,傳統的Ajax請求只能獲取在同一個域名下面的資源,但是HTML5打破了這個限制,允許Ajax發起跨域的請求。瀏覽器是可以發起跨域請求的,比如你可以外鏈一個外域的圖片或者腳本。但是Javascript腳本是不能獲取這些資源的內容的,它只能被瀏覽器執行或渲染。主要原因還是出於安全考慮,瀏覽器會限制腳本中發起的跨站請求。(同源策略, 即JavaScript或Cookie只能訪問同域下的內容)。跨域的解決方案有多重JSONP、Flash、Iframe等,當然還有CORS(跨域資源共享,Cross-Origin Resource Sharing)今天就來了解下CORS的原理,以及如何使用。
一、CORS概述
跨源資源共享標准通過新增一系列 HTTP 頭,讓服務器能聲明那些來源可以通過瀏覽器訪問該服務器上的各類資源(包括CSS、圖片、JavaScript 腳本以及其它類資源)。另外,對那些會對服務器數據造成破壞性影響的 HTTP 請求方法(特別是 GET 以外的 HTTP 方法,或者搭配某些MIME類型的POST請求),標准強烈要求瀏覽器必須先以 OPTIONS 請求方式發送一個預請求(preflight request),從而獲知服務器端對跨源請求所支持 HTTP 方法。在確認服務器允許該跨源請求的情況下,以實際的 HTTP 請求方法發送那個真正的請求。服務器端也可以通知客戶端,是不是需要隨同請求一起發送信用信息(包括 Cookies 和 HTTP 認證相關數據)。
二、CORS原理
例如:域名A(http://a.example)的某 Web 應用程序中通過<img>標簽引入了域名B(http://b.foo)站點的某圖片資源(http://b.foo/image.jpg)。這就是一個跨域請求,請求http報頭包含Origin: http://a.example,如果返回的http報頭包含響應頭 Access-Control-Allow-Origin: http://a.example (或者Access-Control-Allow-Origin: http://a.example),表示域名B接受域名B下的請求,那么這個圖片就運行被加載。否則表示拒絕接受請求。
三、CORS跨域請求控制方法
1.http請求頭
Origin: 普通的HTTP請求也會帶有,在CORS中專門作為Origin信息供后端比對,表明來源域。
Access-Control-Request-Method: 接下來請求的方法,例如PUT, DELETE等等
Access-Control-Request-Headers: 自定義的頭部,所有用setRequestHeader方法設置的頭部都將會以逗號隔開的形式包含在這個頭中
2.http響應頭
然后瀏覽器再根據服務器的返回值判斷是否發送非簡單請求。簡單請求前面講過是直接發送,只是多加一個origin字段表明跨域請求的來源。然后服務器處理完請求之后,會再返回結果中加上如下控制字段
Access-Control-Allow-Origin: 允許跨域訪問的域,可以是一個域的列表,也可以是通配符"*"。這里要注意Origin規則只對域名有效,並不會對子目錄有效。即http://foo.example/subdir/ 是無效的。但是不同子域名需要分開設置,這里的規則可以參照同源策略
Access-Control-Allow-Credentials: 是否允許請求帶有驗證信息,XMLHttpRequest請求的withCredentials標志設置為true時,認證通過,瀏覽器才將數據給腳本程序。
Access-Control-Expose-Headers: 允許腳本訪問的返回頭,請求成功后,腳本可以在XMLHttpRequest中訪問這些頭的信息
Access-Control-Max-Age: 緩存此次請求的秒數。在這個時間范圍內,所有同類型的請求都將不再發送預檢請求而是直接使用此次返回的頭作為判斷依據,非常有用,大幅優化請求次數
Access-Control-Allow-Methods: 允許使用的請求方法,以逗號隔開
Access-Control-Allow-Headers: 允許自定義的頭部,以逗號隔開,大小寫不敏感
四、瀏覽器支持情況
在大部分現代瀏覽器中有所支持,支持(部分支持)CORS協議的瀏覽器有IE8+, Firefox5+, Chrome12+, Safari4+,移動端幾乎全支持。
注:Internet Explorer 8 、9使用 XDomainRequest 對象實現CORS。
五、CORS使用案例
案例環境:客戶端使用jQuery,服務端WebApi(2.2)。因本人使用.net語言,所以服務端就使用webApi來演示了。
首先新建一個webApi項目,這里就不截圖一步步介紹了,然后使用Nuget安裝支持cors的擴展組件,
Install-Package Microsoft.AspNet.WebApi.Cors
然后打開App_Start問價夾下的WebConfig.cs配置文件類,在Register方法中配置一個全局的cors,為了方便我將一些參數配置到web.config配置文件中

<add key="cors_allowOrigins" value="*"/> <add key="cors_allowHeaders" value="*"/> <add key="cors_allowMethods" value="*"/>

var allowOrigins = ConfigurationManager.AppSettings["cors_allowOrigins"]; var allowHeaders = ConfigurationManager.AppSettings["cors_allowHeaders"]; var allowMethods = ConfigurationManager.AppSettings["cors_allowMethods"]; var globalCors = new EnableCorsAttribute(allowOrigins, allowHeaders, allowMethods); config.EnableCors(globalCors);
如果不想使用全局的CORS,可以在某個方法或者ApiController上這樣配置:[EnableCors(origins: "*", headers: "*", methods: "*")],可以使用具體的參數,多個參數以逗號分隔,不用說,肯定英文逗號。origins 域名要帶上http的頂級域名。需要添加 using System.Web.Http.Cors;
一般請求來說,客戶端的AJAX請求不需要做任何改變,只需要服務端稍作改變即可。
客戶端js代碼: apiRootPath是我預先設置的api的頂級域名。
$.ajax({ url: apiRootPath + "api/Account/Register", type: "post", data: { "UserName": mobile, "Password": pwd }, dataType: "json", success: function (data) { if (data.State == true) { RegSuccess(mobile, pwd); } else { $("#errorText").html(data.Message); $("#registerBtn").text("注冊"); } } });
因為我配置了全局的CORS方法,而且服務端沒有特別之處了,和普通的網站(不跨越)寫法一致,這里就不予貼出了。
如果需要對請求進行身份驗證,怎么辦?我們一cookies實現這個驗證。
$.ajax({ url: apiRootPath + "api/Account/Login", type: "post", data: { "UserName": userName, "Password": password }, crossDomain: true, xhrFields: { withCredentials: true }, dataType: "json", success: function (data) { if (data.State == true) { MLogin(userName, password); } else { $("#loginBtn").text("登錄"); $("#errorText").html(data.Message); } } });
注意這個兩句話:crossDomain: true,xhrFields: {withCredentials: true}
六:安全隱患
如果程序猿偷懶將Access-Control-Allow-Origin設置為:Access-Control-Allow-Origin: * 允許任何來自任意域的跨域請求,那么久存在被 DDoS攻擊的可能。
and待補充。。。
七、如有不足,歡迎指出並補充。
轉載請注明出處,謝謝。