2014年1月16日,W3C的Web應(yīng)用工作組(Web Applications Working Group)Web應(yīng)用安全工作組(Web AppSec)聯(lián)合發(fā)布了跨源資源共享(Cross-Origin Resource Sharing)的W3C正式推薦標(biāo)準(zhǔn)(W3C Recommendation)。該標(biāo)準(zhǔn)定義了在必須訪問跨域資源時(shí),瀏覽器與服務(wù)端應(yīng)該如何溝通,它提供一種機(jī)制,允許客戶端(如瀏覽器)對(duì)非源站點(diǎn)的資源發(fā)出訪問請(qǐng)求。所有提供跨源資源請(qǐng)求的API都可以使用本規(guī)范中定義的算法。

出于安全性的考慮,用戶代理(如瀏覽器)通常拒絕跨站的訪問請(qǐng)求,但這會(huì)限制運(yùn)行在用戶代理的Web應(yīng)用通過Ajax或者其他機(jī)制從另一個(gè)站點(diǎn)訪問資源、獲取數(shù)據(jù)??缭促Y源共享(CORS)擴(kuò)充了這個(gè)模型,通過使用自定義的HTTP響應(yīng)頭部(HTTP Response Header),通知瀏覽器資源可能被哪些跨源站點(diǎn)以何種HTTP方法獲得。例如,瀏覽器在訪問 http://example.com 站點(diǎn)的Web應(yīng)用時(shí),Web應(yīng)用如果需要跨站訪問另一站點(diǎn)的資源 http://hello-world.example,就需要使用該標(biāo)準(zhǔn)。http://hello-world.example 在HTTP的響應(yīng)頭部中定義 Access-Control-Allow-Origin: http://example.org,通知瀏覽器允許 http://example.org 跨源從 http://hello-world.example上獲取資源。

 

 

 

CORS 圖解

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

 

過程說明:

1 瀏覽器發(fā)出跨域訪問請(qǐng)求,request header 攜帶 origin

2 服務(wù)器收到請(qǐng)求,服務(wù)器返回response header

3 瀏覽區(qū)檢查reponse header ,如果Access-Control-Allow-Origin : 包含request header的Origin,那么瀏覽器認(rèn)為這是安全操作,允許操作服務(wù)端返回的數(shù)據(jù),否則瀏覽器認(rèn)為非同源數(shù)據(jù),不允許操作。

 

 

目前主流的瀏覽器基本都支持CORS規(guī)范,所以實(shí)現(xiàn)CORS跨域訪問的關(guān)鍵就是Server 端返回的respone中添加 Access-Control-Allow-Origin:對(duì)應(yīng)的請(qǐng)求origin.

 

Web API 如何實(shí)現(xiàn)跨域請(qǐng)求:

 

服務(wù)端代碼

 

類說明:

 

 

  1. CorsAttribute:請(qǐng)求跨域的標(biāo)記

 

  1. CorsMessageHandler:

攔截請(qǐng)求,確認(rèn)請(qǐng)求是否允許跨域,如果允許,那么給response header 添加跨域請(qǐng)求 Access-Control-Allow-Origin,否則返回

{

  "Message": "Cross-origin request denied"

}

 

 

 

代碼:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Threading;using System.Threading.Tasks;using System.Web;using System.Web.Http;using System.Web.Http.Controllers;using System.Net;using System.Diagnostics; 

namespace Hbb0b0.MVC.CORS

{ 

    /// <summary>

    /// 跨域?qū)傩?nbsp;   /// 此處參考    /// asp.net webapi 2    ///

    /// </summary>

    public class CorsAttribute : Attribute

    { 

        /// <summary>

        /// 允許的站點(diǎn)列表        /// </summary>

        public Uri[] AllowOrigins

        {            get;            private set;

        } 

        /// <summary>

        /// 錯(cuò)誤提示        /// </summary>

        public string ErrorMessage

        {            get;            set;

        } 

        /// <summary>

        /// 構(gòu)造函數(shù)        /// </summary>

        /// <param name="allowOrigins"></param>

        public CorsAttribute(params string[] allowOrigins)

        {            this.AllowOrigins = (allowOrigins ?? new string[0]).Select(p => new Uri(p)).ToArray();

        } 

 

        /// <summary>

        /// 獲取請(qǐng)求頭信息,判斷是否請(qǐng)求被允許跨域        /// </summary>

        /// <param name="request"></param>

        /// <param name="headers"></param>

        /// <returns></returns>

        public bool TryEvaluate(HttpRequestMessage request, out IDictionary<string, string> headers)

        {

            headers = null;            string origin = request.Headers.GetValues(HttpHeaderKeys.ORIGIN).FirstOrDefault();

            Uri originUrl = new Uri(origin);            if (this.AllowOrigins.Contains(originUrl))

            {

                headers = this.GenerateResposeHeaders(request);                return true;

            }            this.ErrorMessage = "Cross-origin request denied";            return false;

        } 

        /// <summary>

        /// 添加跨域許可請(qǐng)求頭        /// </summary>

        /// <param name="request"></param>

        /// <returns></returns>

        private IDictionary<string, string> GenerateResposeHeaders(HttpRequestMessage request)

        {            string origin = request.Headers.GetValues("Origin").First();

            Dictionary<string, string> headers = new Dictionary<string, string>();

            headers.Add(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_ORIGIN, origin);            if (request.IsPreflightRequest())

            {

                headers.Add(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_ORIGIN, "*");                string requestHeaders = request.Headers.GetValues(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_HEADERS).FirstOrDefault();                if (!string.IsNullOrEmpty(requestHeaders))

                {

                    headers.Add(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_HEADERS, requestHeaders);

                }

            }            return headers;

        }

 

 

    }    /// <summary>

    /// Http Header keys    /// </summary>

    public class HttpHeaderKeys

    {        public const string ORIGIN = "Origin";        public const string ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";        public const string ACCESS_CONTROL_ALLOW_REQUEST_METHOD = "Access-Control-Allow-Request-Methods";        public const string ACCESS_CONTROL_ALLOW_REQUEST_HEADERS = "Access-Control-Allow-Request-Headers";

    } 

 

    /// <summary>

    /// 判斷是否是非簡單跨域請(qǐng)求擴(kuò)展方法    /// </summary>

    public static class HttpRequestMessageExtensions

    {        public static bool IsPreflightRequest(this HttpRequestMessage request)

        {            return request.Method == HttpMethod.Options &&

                      request.Headers.GetValues(HttpHeaderKeys.ORIGIN).Any() &&

                      request.Headers.GetValues(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_METHOD).Any();

        }

    } 

    public class CorsMessageHandler : DelegatingHandler

    {        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {            //Debugger.Break();
            HttpMethod originalMethod = request.Method;            bool isPreflightRequest = request.IsPreflightRequest();            if (isPreflightRequest)

            {                string method = request.Headers.GetValues(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_HEADERS).FirstOrDefault();

                request.Method = new HttpMethod(method);

            }

 

            HttpConfiguration config = request.GetConfiguration();

 

            HttpControllerDescriptor description = config.Services.GetHttpControllerSelector().SelectController(request);

 

            HttpControllerContext context = new HttpControllerContext(request.GetConfiguration(), request.GetRouteData(), request)

            {

                ControllerDescriptor = description

            };

 

            HttpActionDescriptor actionDescriptor = config.Services.GetActionSelector().SelectAction(context);

 

            CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();            if (corsAttribute == null)

            {                return base.SendAsync(request, cancellationToken);

            }

 

            IDictionary<string, string> headers;

            request.Method = originalMethod;

            HttpResponseMessage response = null;            //判斷請(qǐng)求是否被授權(quán)

            bool authorized = corsAttribute.TryEvaluate(request, out headers); 

            //處理非簡單跨域請(qǐng)求

            if (isPreflightRequest)

            {                if (authorized)

                {

                    response = new HttpResponseMessage(HttpStatusCode.OK);

                }

              

            }            //簡單跨域請(qǐng)求

            else

            {                //如果授權(quán),返回請(qǐng)求資源

                if (authorized)

                {

                    response = base.SendAsync(request, cancellationToken).Result;

                }                //非授權(quán),返回錯(cuò)誤信息

                else

                {

                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage);

                }

            } 

            //添加header

            if (headers!=null)

            {                foreach (var item in headers)

                {

                    response.Headers.Add(item.Key, item.Value);

                }

            } 

 

            return Task.FromResult<HttpResponseMessage>(response);

        }

    }

 

}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

 

 

客戶端代碼:

 

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

@{

    Layout = null;

} 

<!DOCTYPE html>

 <html><head>

    <meta name="viewport" content="width=device-width" />

    <title>View</title>

    <script type="text/javascript" src="~/Scripts/jquery-1.10.2.js"></script>

    <script type="text/javascript">

        $(function () {

            $.ajax({

                type: "POST",

                async: false,

                url: "http://localhost/Hbb0b0.mvc.API/api/StudentAPI/",

                beforeSend: function (xmlHttpRequest) {                    //xmlHttpRequest.setRequestHeader("Origin", "http://hbb0b0notebook:51929/");
                },

                success: function (data) {                    //alert(data.name);
                    console.log(data);

                    getStudentList(data);

                },

 

                error: function () {

                    alert('fail');

                }

            });

 

        }); 

        function getStudentList(list) {

 

            console.debug("GetStudenListCors", list);

            $.each(list, function (index, student) { 

                var html = "<ul>";

                html += "<li> name:" + student.Name + "</li>"

                html += "</ul>";

                $("#divStudentList").append(html);

            });

        }    </script>

 </head><body>

    <div id="divStudentList">

 

    </div>

 

 </body></html>

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

運(yùn)行結(jié)果

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

 

Request Header:

 

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

Response header:

 萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

 

結(jié)論

CORS 作為W3C官方正式的跨域資源訪問方案,不像JSONP 是一種臨時(shí)方案。

CORS 不對(duì)請(qǐng)求類型做限制,get, post 都支持,JSONPzhi只支持get. 所以 CORS比JSONP更通用,更是主流的跨域資源訪問請(qǐng)求解決方案。

http://www.cnblogs.com/hbb0b0/p/7189709.html