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[]).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 框架的好书。

跨域访问之CORS的更多相关文章

  1. Web API 跨域访问(CORS)

    1.在web.config里把“    <remove name="OPTIONSVerbHandler" />  ”删掉. 2. 到nuget上装一个包:    ht ...

  2. SpringBoot 实现前后端分离的跨域访问(CORS)

    序言:跨域资源共享向来都是热门的需求,使用CORS可以帮助我们快速实现跨域访问,只需在服务端进行授权即可,无需在前端添加额外设置,比传统的JSONP跨域更安全和便捷. 一.基本介绍 简单来说,CORS ...

  3. 第20章—跨域访问(CORS)

    spring boot 系列学习记录:http://www.cnblogs.com/jinxiaohang/p/8111057.html 码云源码地址:https://gitee.com/jinxia ...

  4. ASP.NET Web API 跨域访问(CORS)

    一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...

  5. 跨域访问技术CORS(Cross-Origin Resource Sharing)简介

    为什么要用CORS? CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing). 它允许浏览器向跨源服务器,发出XMLHttp ...

  6. 跨域访问JSONP CORS

    一.JSONP 常用的Jquery框架支持jsonp方式请求,该方式只支持GET方法,传参大小有限,而且需要后台根据jsonp的请求方式进行封装结果返回. 其中参数jsonp默认为callback,j ...

  7. ASP.NET Web API 跨域访问(CORS)要注意的地方

    一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...

  8. 支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示

    随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Angularjs的框架来做UI,而数据则由另一个Web Api 的网站项目来支撑.注意,这里是 ...

  9. Web Api 2(Cors)Ajax跨域访问

    支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示   随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Ang ...

随机推荐

  1. ES6解构赋值详解

    文章转载自:http://www.zhufengpeixun.cn/article/167 解构赋值(destructuring assignment)语法是一个 Javascript 表达式,这种语 ...

  2. Java常用集合体系以及相互区别

    Collection集合特点: 1.2.1一些 collection 允许有重复的元素,而另一些则不允许 1.2.2一些 collection 是有序的,而另一些则是无序的 [有序是指与添加的顺序一致 ...

  3. MYSQL和JAVA(课堂笔记)

    MYSQL 数据库管理工具 JAVA  编程语言 数据库驱动(JAVA和MYSQL对接方式) 到官网上下载驱动 加载驱动 import java.sql.Connection;import java. ...

  4. C++进阶引导

    1.C++的用途和意义 t0185b047e29feffc26.jpg 总体来说,C++作为一门软件开发语言,它的流行度是在减少的.主要原因在于语言的复杂和灵活导致软件开发成本提高,这体现在开发周期和 ...

  5. nodejs零基础详细教程1:安装+基础概念

    第一章 建议学习时间2小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  6. 基于FPGA的彩色图像转灰度算法实现

    昨天才更新了两篇博客,今天又要更新了,并不是我垃圾产,只不过这些在上个月就已经写好了,只是因为比赛忙,一直腾不出时间整理出来发表而已,但是做完一件事情总感觉不写一博文总结一下就少点什么,所以之后的一段 ...

  7. 浅析DES与AES、RSA三种典型加密算法的比较

    DES与AES的比较 自DES 算法公诸于世以来,学术界围绕它的安全性等方面进行了研究并展开了激烈的争论.在技术上,对DES的批评主要集中在以下几个方面: 1.作为分组密码,DES 的加密单位仅有64 ...

  8. RedHat6.2 x86手动配置LNMP环境

    因为公司要求用RedHat配,顺便让我练习一下Linux里面的操作什么的. 折腾来折腾去终于搞好了,其实也没那么难嘛.但是也要记录一下. 首先,是在服务器里面用VMware搭建的RedHat6.2 x ...

  9. Thrift生成的bean对象,用java内省操作时注意(自己笔记)

    项目需要,需要使用内省操作,将数据写入thrift生成的bean里,于是按常理getWritedMethod.invoke 结果发现set方法找不到,结果看了下thrift自己生成的bean里,set ...

  10. easygen通用代码生成框架[开源]

    什么东东 用过mybatis的同学都知道,手工写mapper和xml是一件很痛苦的事儿,幸好官方提供了Mybatis-Generator,但是这家伙生成的东西不开放不方便修改,而且项目中的代码生成需求 ...