在制做oneday-music-player的时候要使用ajax向百度音乐的api发送请求,而后出现了
XMLHttpRequest cannot load 'http://....' . No 'Access-Control-Allow-Origin' header is present on the request resource. Origin 'http://....' is therefore not allowed access
,通过搜索发现是受到了同源策略的影响而致使的跨域问题,因此学习一下关于跨域的知识点。javascript
同源策略限制从一个源加载的文档或脚本与另外一个源的文档或脚本进行交互的方式,是隔离潜在恶意文件的重要安全机制。html
两个页面拥有一样的协议、端口(若是指定)和域名时,能够说两个页面是同源的。html5
下表是相对于http://store.company.com/dir/page.html
同源检测的示例:java
url | 结果 | 缘由 |
---|---|---|
http://store.company.com/dir2/other.html |
成功 | |
http://store.company.com/dir/inner/other.html |
成功 | |
https://store.company.com/secure.html |
失败 | 不一样协议(https 和http ) |
http://store.company.com:81/dir/etc.html |
失败 | 不一样端口(81和80) |
http://news.company.com/dir/other.html |
失败 | 不一样域名(news 和store ) |
而若是非同源,则有三种行为会受到限制:git
Cookie是服务器写入浏览器的一小段信息,只有同院的网页才能共享。可是,两个网页一级域名相同,只是二级域名不一样,浏览器容许经过document.domain
共享Cookiegithub
例如,假设文档中的一个脚本在http://store.company.com/dir/page.html
执行如下语句:web
document.domain = "company.com"
此时,http://news.company.com/dir/other.html
和http://store.company.com/dir/other.html
就能够经过document.cookie
来设置或获取Cookie,即共享Cookie。ajax
可是这种方法适用于Cookie和iframe窗口,LocalStorage和IndexDB没法经过这种方法规避同源策略。json
若是两个网页不一样源,就没法拿到对方的DOM,典型的例子是iframe
窗口和window.open
方法打开的窗口,若是和父窗口不一样源,则会报错。canvas
此时若是两个窗口一级域名相同,只是二级域名不一样,那么设置document.domain
属性,就能够规避同源策略。
而对于彻底不一样源的网站,目前有三种方法能够解决跨域窗口之间的通讯问题。
片断标识符(fragment identifier)指的是URL的#后面的部分,即http://store.company.com/dir/other.html#fragment
的#fragment
(location.hash),若是只改变片断标识符,页面不会从新刷新。
父窗口能够把信息写入子窗口的片断标识符,子窗口经过监听hashchange
事件获得通知。
每一个iframe都有包裹它的window,这个window是top window的子窗户,因此天然有window.name
属性,指的是当前窗口的名字,这个属性的最大特色是,不管是否同源,只要在同一个窗口里,窗口内全部页面对window.name都有读写的权限。
window.name的值只能是字符串的形式,这个字符串的最大能容许2M左右甚至更大的一个容量,具体取决于不一样的浏览器。
例如,想要在http://example/a.html
中获取http://company.com/data.html
中的数据,能够在a.html中使用一个隐藏的iframe,将iframe的src首先设置为http://company.com/data.html
,将其window.name设置为所需的数据内容,随后再将这个iframe的src设置为跟a.html页面同一个域的一个页面,否则a.html获取不到该iframe的window.name
这是html5中新引入的一个API,可使用它向其它的window对象发送消息,不管这个window对象属于同源仍是不一样源。
例如,父窗口http://example/a.html
向子窗口http://company.com/data.html
发送消息:
var newWin = window.open('http://company.com/data.html', 'title') newWin.postMessage('Hello World!'. 'http://company.com/data.html')
window.postMessage
方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源,即协议
+端口
+域名
,也能够设置为*
,表示不限制域名。
子窗口向父窗口发送消息的写法相似:
window.opener.postMessage('Nice to see you', 'http://example/a.html')
子窗口和父窗口均可以经过message
时间,监听对方的消息。
window.addEventListener('message', function(e) { // ... }, false)
message
事件的事件对象event
有如下三个属性:
经过window.postMessage
,也能够读写其余窗口的localStorage
同源策略规定,AJAX请求只能发给同源的网址,不然就报错,可是有三种方法能够规避这个限定:
JSONP是服务器与客户端跨源通讯的经常使用方法。基本思想是利用<script>
请求脚本可以跨域访问的特性,先定义了一个回调方法,而后将其做为url参数的一部分发送到服务端,服务端经过字符串拼接的方式将数据包裹在回调方法中,再返回回来。
// 网页动态插入`<script>`元素 function addScriptTag(src) { var script = document.createElement("script") script.setAttribute("type", "text/javascript") srcipt.src = src document.body.appendChild(script) } window.onload = function() { addScriptTag('http://example.com/ip?callback=foo') } function foo(data) { // ... }
WebSocket是一种通讯协议,使用ws://
(非加密)和wss://
(加密)做为协议前缀。该协议不实行同源政策,只要服务支持,就能够经过它进行跨源通讯。
浏览器发出的WebSocket请求的头信息中含有Origin
字段,表示该请求的请求源,即发自哪一个域名。(加入白名单)
跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种使用额外的HTTP头来使一个用户代理从一个不一样于当前站点(域)的服务器获取指定的资源的机制。用户代理使用跨域HTTP请求来获取与当前文档不一样域、不用协议或端口的资源。
出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求。而跨域资源共享(CORS)机制容许web应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在API容器中(例如XMLHttpRequest
或Fetch
)使用CORS,以下降跨域HTTP请求所带来的风险。
跨域资源共享标准容许在下列场景中使用跨域HTTP请求:
@font-face
使用跨域字体资源),所以,网站就能够发布TrueType字体资源,并只容许已受权网站进行跨站调用;浏览器将CORS请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)
只要同时知足如下两大条件,就属于简单请求:
1 请求方法是如下三种方法之一:
- HEAD - GET - POST
2 HTTP的头信息不超出如下几种字段:
- Accept - Accept-Language - Content-Language - Last-Event-ID - Content-Type:(只限三个值:application/x-www-form-urlencoded、multipart/from-data、text/plain)
只要不一样时知足上面两个条件,就属于非简单请求
对于简单请求,浏览器会在请求头部增长一个Origin
字段。这个字段用来讲明本次请求来自哪一个源(协议+域名+端口)。服务器根据这个值决定是否赞成此次请求。
若是Origin
指定的源不在许可范围内,服务器会返回一个正常的HTTP回应。而这个回应的头信息不包含Access-Control-Allow-Origin
字段,从而会抛出错误被XMLHttpRequest
的onerror
函数捕获,(回应的状态码有多是200)。
若是Origin
指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段
Access-Control-Allow-Origin: ... Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: callback Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin
: 必须。值要么是请求时Origin
的值,要么是'*'Access-Control-Allow-Credentials
: 可选。布尔值,决定是否容许发送Cookie,不须要则删除该字段。Access-Control-Expose-Headers
: 可选。CORS请求时,XMLHttpRequest
对象的 getResponseHeader()
方法只能拿到6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。若是想拿到其余字段,就须要在Access-Control-Expose-Header
里面指定。上面的例子指定为callback,则可使用getResponseHeader(callback)
获取callback字段的值。CORS请求默认不发送Cookie和HTTP认证信息。若是要把Cookie发送到服务器,一方面要服务器赞成,指定Access-Control-Allow-Credentials
字段,另外一方面,开发者须要在AJAX请求中设置withCredentials
属性:
var xhr = new XMLHttpRequest() xhr.withCredentials = true
不然,即便服务器赞成发送Cookie,浏览器也不会发送。
须要注意的是,若是要发送Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源策略,只有用服务器域名设置的Cookie才会上传,其余域名的Cookie并不会上传,且跨域的原网页中的document.cookie
操做也没法获取嗷服务器域名下的Cookie。
非简单请求是那种对服务器有特殊要求的请求,好比请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
非简单请求的CORS请求,会在正式通讯以前,增长一次HTTP查询请求,称为“预检”请求。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可使用哪些HTTP操做和头信息字段。只有获得确定答复,浏览器才会发出正式的XMLHttpRequest
请求,不然就报错。
“预检”请求用请求方法是OPTIONS
,表示这个请求是用来询问的。头信息里面,关键字段是Origin
,表示请求来自哪一个源。
还有如下两个特殊字段:
Access-Control-Request-Method
: 必须。列出非简单请求的请求类型Access-Control-Request-Headers
: 非简单请求额外携带的头信息字段。服务器返回的响应:
Access-Control-Allow-Methods: ... Access-Control-Expose-Headers: callback Access-Control-Allow-Credentials: true Access-Control-Max-Age: 1728000
Access-Control-Request-Headers
字段,则Access-Control-Allow-Headers
字段是必需的。它也是一个逗号分隔的字符串,代表服务器支持的全部头信息字段,不限于浏览器在"预检"中请求的字段。CORS与JSONP的使用目的相同,可是比JSONP更强大。
JSONP只支持GET
请求,CORS支持全部类型的HTTP请求。JSONP的优点在于支持老式浏览器,以及能够向不支持CORS的网站请求数据。