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 框架的好书。

  • 相关阅读:
    新闻发布系统之 登陆注销
    readonly和const的区别
    c#中文件流的读写
    解决jsp两种提交方式乱码 的方法
    B/S 和 C/S两种架构
    App.config的典型应用
    接口实现多态
    Spring概念
    在Spring的核心配置文件applicationContext.xml中配置事务,主要配置三大方面:事务管理器、事务通知和定义事务性切面。
    乱码问题解决方案
  • 原文地址:https://www.cnblogs.com/hbb0b0/p/7189709.html
Copyright © 2011-2022 走看看