首页 技术 正文
技术 2022年11月21日
0 收藏 508 点赞 2,939 浏览 6403 个字

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 图解

跨域访问之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[]).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>

运行结果

跨域访问之CORS

Request Header:

跨域访问之CORS

Response header:

跨域访问之CORS

跨域访问之CORS

结论

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

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

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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,105
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,582
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,429
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,836
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,919