跨域访问之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 如何实现跨域请求:
服务端代码
类说明:
- CorsAttribute:请求跨域的标记
- 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的更多相关文章
- Web API 跨域访问(CORS)
1.在web.config里把“ <remove name="OPTIONSVerbHandler" /> ”删掉. 2. 到nuget上装一个包: ht ...
- SpringBoot 实现前后端分离的跨域访问(CORS)
序言:跨域资源共享向来都是热门的需求,使用CORS可以帮助我们快速实现跨域访问,只需在服务端进行授权即可,无需在前端添加额外设置,比传统的JSONP跨域更安全和便捷. 一.基本介绍 简单来说,CORS ...
- 第20章—跨域访问(CORS)
spring boot 系列学习记录:http://www.cnblogs.com/jinxiaohang/p/8111057.html 码云源码地址:https://gitee.com/jinxia ...
- ASP.NET Web API 跨域访问(CORS)
一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...
- 跨域访问技术CORS(Cross-Origin Resource Sharing)简介
为什么要用CORS? CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing). 它允许浏览器向跨源服务器,发出XMLHttp ...
- 跨域访问JSONP CORS
一.JSONP 常用的Jquery框架支持jsonp方式请求,该方式只支持GET方法,传参大小有限,而且需要后台根据jsonp的请求方式进行封装结果返回. 其中参数jsonp默认为callback,j ...
- ASP.NET Web API 跨域访问(CORS)要注意的地方
一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...
- 支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示
随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Angularjs的框架来做UI,而数据则由另一个Web Api 的网站项目来支撑.注意,这里是 ...
- Web Api 2(Cors)Ajax跨域访问
支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示 随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Ang ...
随机推荐
- Finding Similar Users-Euclidean Distance Score
Purpose: Finding Similar Users Method: Euclidean Distance Score ex2.py critics={'Lisa Rose': {'Lady ...
- Scrapy中使用Django的Model访问数据库
Scrapy中使用Django的Model进行数据库访问 当已存在Django项目的时候,直接引入Django的Model来使用比较简单 # 使用以下语句添加Django项目的目录到path impo ...
- java之内部类
最近学了java,对内部类有一点拙见,现在分享一下 所谓内部类(nested classes),即:面向对象程序设计中,可以在一个类的内部定义另一个类. 内部类不是很好理解,但说白了其实也就是一个类中 ...
- [Leetcode] Sort, Hash -- 274. H-Index
Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...
- map,zip,reduce函数
lt=range(5,10) lw=range(8,13) def mul(a,b): return a*b def mul_list(param1,param2): return_list=[] f ...
- springMvc <form action="">提交跳转路径问题
表单提交后,action的URL写的是, login/login.do,每次跳转后都变成login/ login/login.do,很显然是相对路径没有搞清楚. 应该弄清楚相对路径,并在JSP代码中加 ...
- js获取地址栏某个参数
一.获取单个参数: 若地址栏URL为:abc.html?id=123 function getString(){ var locurl=location.href; var sta ...
- CPU-Z五大主要功能及使用方法初步了解
CPU-Z这款软件除了具有查看CPU温度这个功能之外,还有很多其他的功能.今天就和小编一起去看看CPU-Z的5大功能以及他们的使用方法吧! CPU信息标签页 CPU-Z介绍: CPU-Z是一款著名的免 ...
- 【原】vue单文件组件互相通讯
在vue中,我们可以把一个页面各个部分单独封装起来,做成各种小组件,然后加载,这样的好处是代码维护起来比较容易,重复的代码多处调用! 在一个vue项目中,单文件组件以.vue形式文件命名 每个组件之间 ...
- Java基本之数据类型
一.创建一个简单的Java应用程序 public class Code { public static void main(String[]args) { System.out.println(&qu ...