zoukankan      html  css  js  c++  java
  • 跨域访问之CORS

    CORS:定义

    2014年1月16日,W3C的Web应用工作组(Web Applications Working Group)Web应用安全工作组(Web AppSec)联合发布了跨源资源共享(Cross-Origin Resource Sharing)的W3C正式推荐标准(W3C Recommendation)。该标准定义了在必须访问跨域资源时,浏览器与服务端应该如何沟通,它提供一种机制,允许客户端(如浏览器)对非源站点的资源发出访问请求。所有提供跨源资源请求的API都可以使用本规范中定义的算法。

    出于安全性的考虑,用户代理(如浏览器)通常拒绝跨站的访问请求,但这会限制运行在用户代理的Web应用通过Ajax或者其他机制从另一个站点访问资源、获取数据。跨源资源共享(CORS)扩充了这个模型,通过使用自定义的HTTP响应头部(HTTP Response Header),通知浏览器资源可能被哪些跨源站点以何种HTTP方法获得。例如,浏览器在访问 http://example.com 站点的Web应用时,Web应用如果需要跨站访问另一站点的资源 http://hello-world.example,就需要使用该标准。http://hello-world.example 在HTTP的响应头部中定义 Access-Control-Allow-Origin: http://example.org,通知浏览器允许 http://example.org 跨源从 http://hello-world.example上获取资源。

    CORS 图解

    过程说明:

    1 浏览器发出跨域访问请求,request header 携带 origin

    2 服务器收到请求,服务器返回response header

    3 浏览区检查reponse header ,如果Access-Control-Allow-Origin : 包含request header的Origin,那么浏览器认为这是安全操作,允许操作服务端返回的数据,否则浏览器认为非同源数据,不允许操作。

    目前主流的浏览器基本都支持CORS规范,所以实现CORS跨域访问的关键就是Server 端返回的respone中添加 Access-Control-Allow-Origin:对应的请求origin.

    Web API 如何实现跨域请求:

    服务端代码

    类说明:

    1. CorsAttribute:请求跨域的标记
    1. CorsMessageHandler:

    拦截请求,确认请求是否允许跨域,如果允许,那么给response header 添加跨域请求 Access-Control-Allow-Origin,否则返回

    {

      "Message": "Cross-origin request denied"

    }

    代码:

    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>
    
        /// 跨域属性
    
        /// 此处参考
    
        /// asp.net webapi 2
    
        ///
    
        /// </summary>
    
        public class CorsAttribute : Attribute
    
        {
    
     
    
            /// <summary>
    
            /// 允许的站点列表
    
            /// </summary>
    
            public Uri[] AllowOrigins
    
            {
    
                get;
    
                private set;
    
            }
    
     
    
            /// <summary>
    
            /// 错误提示
    
            /// </summary>
    
            public string ErrorMessage
    
            {
    
                get;
    
                set;
    
            }
    
     
    
            /// <summary>
    
            /// 构造函数
    
            /// </summary>
    
            /// <param name="allowOrigins"></param>
    
            public CorsAttribute(params string[] allowOrigins)
    
            {
    
                this.AllowOrigins = (allowOrigins ?? new string[0]).Select(p => new Uri(p)).ToArray();
    
            }
    
     
    
     
    
            /// <summary>
    
            /// 获取请求头信息,判断是否请求被允许跨域
    
            /// </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>
    
            /// 添加跨域许可请求头
    
            /// </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>
    
        /// 判断是否是非简单跨域请求扩展方法
    
        /// </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;
    
                //判断请求是否被授权
    
                bool authorized = corsAttribute.TryEvaluate(request, out headers);
    
     
    
                //处理非简单跨域请求
    
                if (isPreflightRequest)
    
                {
    
                    if (authorized)
    
                    {
    
                        response = new HttpResponseMessage(HttpStatusCode.OK);
    
                    }
    
                  
    
                }
    
                //简单跨域请求
    
                else
    
                {
    
                    //如果授权,返回请求资源
    
                    if (authorized)
    
                    {
    
                        response = base.SendAsync(request, cancellationToken).Result;
    
                    }
    
                    //非授权,返回错误信息
    
                    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);
    
            }
    
        }
    
     
    
    }

     

    客户端代码:

     

    @{
    
        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>

    运行结果

    Request Header:

    Response header:

     

    结论

    CORS 作为W3C官方正式的跨域资源访问方案,不像JSONP 是一种临时方案。

    CORS 不对请求类型做限制,get, post 都支持,JSONPzhi只支持get. 所以 CORS比JSONP更通用,更是主流的跨域资源访问请求解决方案。

    本文参考了<<asp.net  webapi 2  框架解密>>,此书的确是深入剖析asp.net 框架的好书。

  • 相关阅读:
    Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task(枚举/最大连续子序列)
    Educational Codeforces Round 88 (Rated for Div. 2) A. Berland Poker(数学)
    Educational Codeforces Round 88 (Rated for Div. 2) E. Modular Stability(数论)
    Educational Codeforces Round 88 (Rated for Div. 2) C. Mixing Water(数学/二分)
    Codeforces Round #644 (Div. 3)
    Educational Codeforces Round 76 (Rated for Div. 2)
    Educational Codeforces Round 77 (Rated for Div. 2)
    Educational Codeforces Round 87 (Rated for Div. 2)
    AtCoder Beginner Contest 168
    Codeforces Round #643 (Div. 2)
  • 原文地址:https://www.cnblogs.com/hbb0b0/p/7189709.html
Copyright © 2011-2022 走看看