同源策略与JSONP
[CORS:跨域资源共享] 同源策略与JSONP
Web API普遍采用面向资源的REST架构,将浏览器最终执行上下文的JavaScript应用Web API消费者的重要组成部分。“同源策略”限制了JavaScript的跨站点调用,这必然导致Web API不能垮域提供资源。如果Web API仅限于为“同源客户端”提供资源,那么它都对不起自己的名字,因为Web本身是一个开放的协议。那么ASP.NET Web API通过怎样的方式来实现跨域资源共享呢?
同源策略
浏览器是访问Internet的工具,也是客户端应用的宿主,它为客户端应用提供一个寄宿和运行的环境。而这里所说的应用,基本是指在浏览器中执行的客户端JavaScript程序。虽然是一种解释性的脚本语言,JavaScript其实是无比强大的,原则上来讲它可以做任何事。但是在能够在JavaScript脚本并不都是值得信赖的,所以浏览器必须对JavaScript的执行作相应的限制,这就是我们接下来着重介绍的“同源策略(Same Origin Policy)”。
同源策略是浏览器的一项最为基本同时也是必须遵守的安全策略,毫不夸张地说,浏览器的整个安全体系均建立在此之上。同源策略的存在,限制了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝。所谓的“同源”,必须要求相应的URI在如下3个方面均是相同的。术语“源(Origin)”在中文表达中显得有点突兀,所以在接下来的内容中,我们更多地会采用“站点(Site)”或者“域(Domain)”这样的说法,在未作特别说明的情况下均与“源”表达相同的意思。
- 主机名称(域名/子域名或者IP地址)
- 端口号
- 网络协议(Scheme,分别采用“http”和“https”协议的两个URI被视为不同源)
值得一提的是,对于一段JavaScript脚本来说,其“源”与它存储的地址无关,而取决于脚本被加载的页面。比如我们在某个页面中通过如下所示的<script>标签引用了来源于不同地方(“http://www.artech.com/”和“http://www.jinnan.me/”)的两个JavaScript脚本,它们均与当前页面同源。实际上接下来介绍的基于JSONP跨域资源共享就是利用了这个特性。
1: <script src="http://www.artech.com/scripts/common.js"></script>
2: <script src="http://www.jinnan.me/scripts/utility.js"></script>
除了<script>标签,HTML还具有其它一些具有src属性的标签(比如<img>、<iframe>和<link>等),它们均具有跨域加载资源的能力,所以同源策略对它们不做限制。对于这些具有src属性的HTML标签来说,标签的每次加载都意味着针对目标地址的一次HTTP-GET请求。
同源策略以及跨域资源共享在大部分情况下针对的是Ajax请求。同源策略主要限制了通过XMLHttpRequest实现的Ajax请求,如果请求的是一个“异源”地址,浏览器将不允许读取返回的内容,我们可以通过一个简单的实例来演示这一点。
实例演示:跨域调用Web API
接下来我们通过于一个简单的实例来演示同源策略针对跨域Ajax请求的限制。如右图所示,我们利用Visual Studio在同一个解决方案中创建了两个Web应用。从项目名称可以看出,WebApi和MvcApp分别为ASP.NET Web API和MVC应用,后者是Web API的调用者。我们直接采用默认的IIS Express作为两个应用的宿主,并且固定了端口号:WebApi和MvcApp的端口号分别为“3721”和“9527”,所以指向两个应用的URI肯定不可能是同源的。
我们在WebApi应用中定义了如下一个继承自ApiController的ContactsController类型,它具有的唯一Action方法GetAllContacts返回一组联系人列表。由于具体返回的数据类型为JsonResult<IEnumerable<Contact>>,所以联系人 列表以JSON格式被序列化。
1: public class ContactsController : ApiController
2: {
3: public IHttpActionResult GetAllContacts()
4: {
5: Contact[] contacts = new Contact[]
6: {
7: new Contact{ Name="张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},
8: new Contact{ Name="李四", PhoneNo="456", EmailAddress="lisi@gmail.com"},
9: new Contact{ Name="王五", PhoneNo="789", EmailAddress="wangwu@gmail.com"},
10: };
11: return Json<IEnumerable<Contact>>(contacts);
12: }
13: }
14:
15: public class Contact
16: {
17: public string Name { get; set; }
18: public string PhoneNo { get; set; }
19: public string EmailAddress { get; set; }
20: }
接下来们在MvcApp应用中定义如下一个HomeController,默认的Action方法Index会将对应的View呈现出来。
1: public class HomeController : Controller
2: {
3: public ActionResult Index()
4: {
5: return View();
6: }
7: }
如下所示的是Action方法Index对应View的定义。我们的目的在于:当页面成功加载之后以Ajax请求的形式调用上面定义的Web API获取联系人列表,并将自呈现在页面上。如下面的代码片断所示,Ajax调用和返回数据的呈现是通过调用jQuery的getJSON方法完成的。
1: <html>
2: <head>
3: <title>联系人列表</title>
4: <script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>
5: </head>
6: <body>
7: <ul id="contacts"></ul>
8: <script type="text/javascript">
9: $(function ()
10: {
11: var url = "http://localhost:3721/api/contacts";
12: $.getJSON(url, null, function (contacts) {
13: $.each(contacts, function (index, contact)
14: {
15: var html = "<li><ul>";
16: html += "<li>Name: " + contact.Name + "</li>";
17: html += "<li>Phone No:" + contact.PhoneNo + "</li>";
18: html += "<li>Email Address: " + contact.EmailAddress + "</li>";
19: html += "</ul>";
20: $("#contacts").append($(html));
21: });
22: });
23: }
24: );
25: </script>
26: </body>
27: </html>
如果运行我们的程序,我们将会得到如右图所示的空白页面,这就是“同源策略”导致的后果。值得一提的是,我们并不会得到任何的错误信息,这是因为大部分浏览器针对同源策略的支持都是隐性和透明的。如果开发人员对此不了解的话,根本想不明白错误根源何在。
如果我们采用Fiddler来监测页面加载过程中发送的请求和接收到的响应,我们会发现针对Web API调用的Ajax请求被成功发送,并且以JSON格式表示的联系人列表会被成功接收,请求和响应的内容如下所示。这实际上说明支持同源策略的浏览器其实并不会阻止跨域请求的发送和响应的接收,它仅仅是阻止程序获取和操作返回的数据而已。
1: GET http://localhost:3721/api/contacts HTTP/1.1
2: Host: localhost:3721
3: Connection: keep-alive
4: Cache-Control: max-age=0
5: Accept: application/json, text/javascript, */*; q=0.01
6: Origin: http://localhost:9527
7: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
8: Referer: http://localhost:9527/
9: Accept-Encoding: gzip,deflate,sdch
10: Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4
11:
12:
13: HTTP/1.1 200 OK
14: Cache-Control: no-cache
15: Pragma: no-cache
16: Content-Length: 205
17: Content-Type: application/json; charset=utf-8
18: Expires: -1
19: Server: Microsoft-IIS/8.0
20: X-AspNet-Version: 4.0.30319
21: X-SourceFiles: =?UTF-8?B?RTpc5oiR55qE6JGX5L2cXEFTUC5ORVQgV2ViIEFQSeahhuaetuaPreenmFxOZXcgU2FtcGxlc1xDaGFwdGVyIDE0XFMxNDAxXFdlYkFwaVxhcGlcY29udGFjdHM
22: X-Powered-By: ASP.NET
23: Date: Mon, 02 Dec 2013 01:47:53 GMT
24:
25: [{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":"wangwu@gmail.com"}]
对于上面给出的针对Web API的Ajax请求的内容,我们可以看到它具有一个名为“Origin”的报头。该报头值表示请求页面的所在的站点(http://localhost:9527),它可以看成是浏览器对CORS(Cross-Origin Resource Sharing)规范的支持。
采用JSONP实现跨域资源共享
上面我们已经说过:JavaScript脚本的源决定于其被加载的页面,而不是其存储的地址。对于一段通过<script>标签的src属性加载的JavaScript脚本,它与当前页面同源。对于上面我们演示的实例来说,如果我们按照如下的方式来定义View:联系人列表的呈现单独定义在listContacts函数中(参数contacts表示联系人列表),并将Web API的地址置于<script>标签的src属性中来间接地调用它。
1: <html>
2: <head>
3: <title>联系人列表</title>
4: <script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>
1: <script type="text/javascript">
2: function listContacts(contacts)
3: {
4: $.each(contacts, function (index, contact) {
5: var html = "<li><ul>";
6: html += "<li>Name: " + contact.Name + "</li>";
7: html += "<li>Phone No:" + contact.PhoneNo + "</li>";
8: html += "<li>Email Address: " + contact.EmailAddress + "</li>";
9: html += "</ul>";
10: $("#contacts").append($(html));
11: });
12: }
1: </script>
2: </head>
3: <body>
4: <ul id="contacts"></ul>
5: <script type="text/javascript" src="http://localhost:3721/api/contacts?callback=listContacts"></script>
5: </body>
6: </html>
如果请求地址“http://localhost:3721/api/contacts?callback=listContacts”能够返回如下的内容,即返回的不是以JSON表示的数据,而是针对该数据的方法调用,毫无疑问联系人列表能够顺利呈现在页面上。这种将JSON对象填充(Padding)到某个JavaScript回调方法将数据转换成针对数据的操作语句的形式就是JSONP(JSON Padding)。
1: listContacts([{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":"wangwu@gmail.com"}]);
为了让定义在ContactsController的Action方法GetAllContacts返回如上所示的内容,我们可以对其相应的改动来完成。如下面的代码片断所示,我们将GetAllContacts的返回类型替换成HttpResponseMessage,其参数callback表示填充的JavaScript回调函数。在该方法中,我们利用JavaScriptSerializer对Contact列表对象进行序列化,并将得到的内容填充到回调函数中从而得到如上所示的内容。方法最终返回具有此主体内容的HttpResponseMessage对象,响应主体内容的媒体类型被设置为“text/javascript”。
1: public class ContactsController : ApiController
2: {
3: public HttpResponseMessage GetAllContacts(string callback)
4: {
5: Contact[] contacts = new Contact[]
6: {
7: new Contact{ Name="张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},
8: new Contact{ Name="李四", PhoneNo="456", EmailAddress="lisi@gmail.com"},
9: new Contact{ Name="王五", PhoneNo="789", EmailAddress="wangwu@gmail.com"},
10: };
11: JavaScriptSerializer serializer = new JavaScriptSerializer();
12: string content = string.Format("{0}({1})", callback, serializer.Serialize(contacts));
13: return new HttpResponseMessage(HttpStatusCode.OK)
14: {
15: Content = new StringContent(content, Encoding.UTF8, "text/javascript")
16: };
17: }
18: }
如果现在运行我们的程序,通过“跨域”(其实不是)调用Web API得到的联系人列表就会按照如右图所示的效果呈现出来。JSONP仅仅是利用<script>的src标签加载的脚本不受同源策略约束而采取的一种编程技巧,其本身并不是一种官方协议。并且并非所有类型跨域调用都能采用JSONP的方式来解决(由于所有具有src属性的HTML标签均通过HTTP-GET的方式来加载目标资源,这决定了JSONP只适用于HTTP-GET请求),所以我们必须寻求一种更好的解决方案。
[1] 同源策略与JSONP
[2] W3C的CORS规范
[3] 通过自定义HttpMessageHandler实现跨域资源共享
[4] CORS在ASP.NET Web API中的实现
同源策略与JSONP的更多相关文章
- [CORS:跨域资源共享] 同源策略与JSONP
Web API普遍采用面向资源的REST架构,将浏览器最终执行上下文的JavaScript应用Web API消费者的重要组成部分."同源策略"限制了JavaScript的跨站点调用 ...
- 同源策略与 JSONP CORS
同源策略与 JSONP CORS 同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以 ...
- 同源策略和JSONP(概念性)
同源策略 浏览器有一个很重要的概念——同源策略(Same-Origin Policy). 所谓同源是指,域名,协议,端口相同.不同源的客户端脚本(javascript.ActionScript)在没明 ...
- Ajax跨域请求 同源策略与Jsonp
同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上的 ...
- 同源策略和Jsonp、CORS
一.同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之 ...
- javascript 同源策略和 JSONP 的工作原理
同源策略 同源策略是一个约定,该约定阻止当前脚本获取或操作另一域的内容.同源是指:域名.协议.端口号都相同. 简单地说,A 服务器下的 a 端口执行 ajax 程序,不能获取 B 服务器或者 A 服务 ...
- 第十九篇 同源策略与Jsonp
同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上的 ...
- 同源策略与JSONP劫持原理
同源策略 浏览器中有两个安全机制,一个浏览器沙盒(Sandbox),另一个就是同源策略(Same Origin Policy,简称SOP) ,下面介绍同源策略.同源是指同协议.同域名.同端口,必须三同 ...
- ajax同源策略,jsonP跨域访问
浏览器处于安全性的考虑,要求ajax请求,必须满足同源策略 规定:访问的协议://域名:端口号都相同时满足同源策略,浏览器可以正确解析数据,否则如果有一项不满足要求,则属于跨域访问,浏览器可以正常获取 ...
随机推荐
- kettle于javascript步骤错误处理
javascript步骤错误处理 假设你熟悉kettle误差特性转换.你可能想知道javascript步骤如何使用.骤用户界面机制是同样的,在javascript步骤右击,选择"定义错误处理 ...
- Nyoj 城市平乱(图论)
描述 南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市. 他在用这N个部队维护着M个城市的治安,这M个城市分别编号从1到M. 现在,小工军师告诉南将军,第K号城市发生了暴乱,南将军从各个部队都 ...
- java流下载
@RequestMapping("/pluginDownload") public void pluginDownload(HttpServletResponse response ...
- 基于Jquery的多彩二维码的生成
Demo效果图: 源代码: @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name=&quo ...
- hadoop编程小技巧(5)---自己定义输入文件格式类InputFormat
Hadoop代码測试环境:Hadoop2.4 应用:在对数据须要进行一定条件的过滤和简单处理的时候能够使用自己定义输入文件格式类. Hadoop内置的输入文件格式类有: 1)FileInputForm ...
- unicode编码和中国的相互转换
如果你的原始文件1.properties(该文件的编码中国).要转换unicode的 在cmd通过进入你在哪里在这种类型的文件夹: native2ascii -encoding gb2312 1.pr ...
- Atitit.异步编程 java .net php python js 对照
Atitit.异步编程 java .net php python js 的比較 1. 1.异步任务,异步模式, APM模式,, EAP模式, TAP 1 1.1. APM模式: Beg ...
- Spark集群搭建简配+它到底有多快?【单挑纯C/CPP/HADOOP】
最近耳闻Spark风生水起,这两天利用休息时间研究了一下,果然还是给人不少惊喜.可惜,笔者不善JAVA,只有PYTHON和SCALA接口.花了不少时间从零开始认识PYTHON和SCALA,不少时间答了 ...
- UITabBarControler解决旋转问题
在遇到开发项目中播放视频,播放视频回列表后,,列表 NavigationController 里边, NavigationController 在 UItabBarController 里边,不要旋转 ...
- ios MKNetworkKit 的使用
常用框架比如:AFNetworking,ASIHttpRequest,SDWebImage,MKNetWorKit等. iOS5已出来这么久了,而ASIHttpRequest的作者已经申明不更新了,在 ...