什么是同源策略?javascript
同源策略是浏览器的一项最为基本同时也是必须遵照的安全策略。同源策略的存在,限制了“源”自A的脚本只能操做“同源”页面的DOM,“跨源”操做来源于B的页面将会被拒绝。所谓的“同源”,必需要求相应的URI在以下3个方面均是相同的。java
对于一段JavaScript脚原本说,其“源”与它存储的地址无关,而取决于脚本被加载的页面。好比在某个页面中经过<script>标签引用了来源于不一样地方的两个JavaScript脚本,它们均与当前页面同源。ajax
<script src="http://www.a.com/scripts/a.js"></script>
<script src="http://www.b.me/scripts/b.js"></script>
除了<script>标签其它一些具备src属性的标签(好比<img>),它们均具备跨域加载资源的能力,因此同源策略对它们不作限制。编程
同源策略主要限制了经过XMLHttpRequest实现的Ajax请求,若是请求的是一个“异源”地址,浏览器将不容许读取返回的内容。json
JSONP实现跨域资源共享api
经过<script>标签的src属性加载的JavaScript脚本跨域
<script type="text/javascript" src="http://localhost:8080/api/test?callback=test"></script> <script type="text/javascript"> function test(arg){ } </script>
JSONP是利用<script>的src标签加载的脚本不受同源策略约束而采起的一种编程技巧,不是一种官方协议。因为具备src属性的HTML标签均经过HTTP-GET的方式来加载目标资源,JSONP只适用于HTTP-GET请求。浏览器
咱们能够利用JQuery发送JSONP的Ajax跨域请求,调用$.ajax方法并将dataType参数设置为“jsonp”缓存
<script type="text/javascript"> $(function () { $.ajax({ dataType : "jsonp" }); }); </script>
CORS(Cross-Origin Resource Sharing)安全
基于Web的资源共享涉及到两个基本的角色,即资源的提供者和消费者。即显示在浏览器中的某个Web页面经过调用Web API的方式来获取它所需的资源,资源提供者为Web API自己,经过发送Ajax请求来调用Web API的JavaScript程序为资源的消费者。
CORS旨在定义一种规范让浏览器在接收到从提供者获取的资源时可以正决定是否应该将此资源分发给消费者做进一步处理。CROS利用资源提供者的显式受权来决定目标资源是否应该与消费者共享。浏览器须要获得提供者的受权以后才会将其提供的资源分发给消费者。
若是浏览器 自身提供对CROS的支持,由它发送的请求会携带一个名为“Origin”的报头代表请求页面所在的站点。
资源获取请求被提供者接收以后,它能够根据该报头肯定提供的资源须要共享给谁。资源提供者的受权经过一个名为“Access-Control-Allow-Origin”的响应报头来承载,其报头值表示获得受权的站点。通常来讲,若是资源的提供者承认了当前请求的“Origin”报头携带的站点,那么它会将该站点做为“Access-Control-Allow-Origin”响应报头的值。
当浏览器接收到包含资源的响应以后,会提取此“Access-Control-Allow-Origin”响应报头的值。若是此值为“*”或者包含的源列表包含此前请求的源(即请求的“Origin”报头值),意味着资源的消费者获取了提供者获取和操做资源的权限,因此浏览器会容许JavaScript程序操做获取的资源。若是此响应报头不存在或者其值为“null”,客户端JavaScript程序针对资源的操做会被拒绝。
简单(HTTP)方法(Simple Method)”、“简单(请求)报头(Simple Header)”和“自定义请求报头(Author Request Header/Custom Request Header)”
CORS规范将GET、HEAD和POST这三个HTTP方法视为“简单HTTP方法”,而将请求报头Accept, Accept-Language, Content-Language以及采用以下三种媒体类型的报头Content-Type称为“简单请求报头”
由JavaScript程序自行添加的报头(好比调用XMLHttpRequest的setRequestHeader方法能够为生成的Ajax请求添加任意报头),被称为“自定义报头”。
简单请求/非简单跨域资源请求
CORS规范将服务以下条件的跨域资源请求划分为简单请求:请求采用简单HTTP方法,而且其自定义请求报头空或者全部自定义请求报头均为简单请求报头。
对于简单跨域资源请求来讲,浏览器将两个步骤(取得受权和获取资源)合二为一。若是针对请求的处理过程会涉及到对资源的改变,这样作就会有问题了。按照CORS规范的规定,浏览器应该采用一种被称为“预检”的机制来完成非简单跨域资源请求。
所谓预检机制就是说浏览器在发送真正的跨域资源请求前,先发送一个预检请求。预检请求为一个采用HTTP-OPTIONS方法的请求,这是一个不包含主体的请求,同时用户凭证相关的报头也会被剔除。基于真正资源请求的一些辅助受权的信息会包含在此预检请求的相应报头中。除了表明请求页面所在站点的“Origin”报头以外,以下所示的是两个典型的请求报头。
资源的提供者在接收到预检请求以后,根据其提供的相关报头进行受权检验,具体的检验逻辑即包括肯定请求站点是否值得信任,以及请求采用HTTP方法和自定义报头是否被容许。若是预检请求没有经过受权检验,资源提供者通常会返回一个状态为“400, Bad Reuqest”的响应。反之则会返回一个状态为“200, OK”的响应,受权相关信息会包含在响应报头中。除了上面介绍的“Access-Control-Allow-Origin”报头以外,预检请求的响应还具备以下3个典型的报头。
浏览器在接收到预检响应以后,会根据响应报头肯定后续发送的真正跨域资源请求是否会被接受,相关的检验包括针对服务端容许站点以及HTTP方法和自定义请求报头(利用响应报头“Access-Control-Allow-Methods”和“Access-Control-Allow-Headers”)的检验。具体的检验逻辑以下
只有在肯定服务端必定会接受的状况下,浏览器才会发送真正跨域资源请求。预检响应结果会被浏览器缓存,在“Access-Control-Max-Age”报头设定的时间内,缓存的结果将被浏览器用户进行受权检验,因此在此期间不会再有预检请求发送。
用户凭证
在默认状况下,利用XMLHttpReuqest发送的Ajax请求不会携带用户凭证相关的敏感信息,这里的用户凭证类型包括Cookie、HTTP-Authentication报头以及客户端X.509证书(采用支持客户端证书的TLS/SSL)等。若是须要用户凭证附加到Ajax请求上,须要将XMLHttpReuqest的withCredentials 属性设置为True。
W3C的CORS规范,服务端利用响应报头“Access-Control-Allow-Credentials”来代表自身是否支持用户凭证。
JavaScript程序利用一个withCredentials属性为true的XMLHttpReuqest发送了一个跨域资源请求,可是浏览器获得的响应中不具备一个值为“true”的响应报头“Access-Control-Allow-Credentials”,它对获取资源的操做将会浏览器拒绝。
ps:本文参考摘要《ASP.NET Web API 2框架揭秘》一书