跨域访问之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 ...
随机推荐
- R笔记(1):formula和Formula
#####开一个新的系列.关于R的一些笔记,就是遇到过的一些问题的简单整理.可能很基本,也可能没什么大的用处,作为一个记录而已.------------------------------------ ...
- 02-C#(基础)基本的定义和说明
C#程序或DLL的源码是一组类型的声明 类:类型是一种模板,可以把类型想象成一个用来创建数据结构的模板.模板本身并不是数据结构,但它详细说明了该模板构造的对象的特征. 命名空间:它是一种把相关的类型声 ...
- <bits/stdc++.h>头文件介绍(包含源代码)
注:转自http://blog.csdn.net/charles_dong2/article/details/56909347,同为本人写的,有部分修改. 之前在一个小OJ上刷题时发现有人是这么写的: ...
- 保存和恢复 Android Fragment 的状态
经过几年在 Android 应用开发中应用 Fragment 的努力之后,我必须要说尽管Fragment的概念非常优秀,但是它也同时带来了一堆问题.当我们处理实例的状态保存时就需要特别一件一件地修护好 ...
- UX2 beta 3正式发布!!
UX浏览服务是为了加速浏览网页而开发的浏览服务,它解决了WebView的一系列问题,它能够在网络差的情况下快速的浏览,比webview快一倍以上,是webview的优化代替方案.它拥有完善的缓存管理策 ...
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...
- JDK并发包
JDK5之后引进了并发包java.util.concurrent,让并发的开发更加可控,更加简单.所以有必要好好学习下,下面从同步控制.并发容器.线程池三部分来详细了解它. 1. 各种同步控制工具的使 ...
- 移动平台Unity3D 应用性能优化
WeTest 导读 做了大概半年多VR应用了,VR由于双眼double渲染的原因,对性能的优化要求比较高,在项目的进展过程中,总结了一些关于移动平台上Unity3D的性能优化经验,供分享. 一.移动平 ...
- 初学unslider
1.关于unslider下载 官方提供的下载页面是http://www.bootcss.com/p/unslider/,但总是进不了下载页面,所以我就查看主页的源码,找到unslider.min.js ...
- solr学习笔记-开篇
由于工作需要,近期接触了一下全文检索的相关技术,从lucenen到solr开始慢慢一路摸爬滚打,仅以此文记录自己的学习里程和记录下各种坑. 本次学习基于以下环境版本: java7,solr5.5.4( ...