CORS
实现CORS需要浏览器和服务器的共同协作。对于前端开发者来说,CORS通信与同源的AJAX在代码方面没有差别。浏览器一旦发现AJAX请求跨源,就会自动添加附加一些请求头信息,有时候还会多出一次附加的请求,但用户不会察觉。
因此,CORS通信的关键就在于后端代码的实现,只要服务器实现了CORS接口,就可以跨源通信。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
- 简单请求
- 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
- HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
- 请求方法是以下三种方法之一:
- 非简单请求
- 凡是不同时满足上面两个条件,就属于非简单请求
两种请求浏览器对他们的处理方式也不一样
简单请求处理流程
(发起AJAX请求) ==> (浏览器发现请求为跨源AJAX简单请求)==>(浏览器自动在头信息里添加Origin字段
) ==>(服务器根据收到的Origin字段
来决定是否同意这次请求)==> (浏览器得到回应,根据返回的头信息没有包含Access-Control-Allow-Origin
字段判断本次CORS请求是否成功)
Origin字段: 用来说明,本次请求来自哪个源(协议 + 域名 + 端口)
非简单请求处理流程
(向自动发出预检请求)==> (服务器回应预检请求)==> (服务器同意则会返回一个带Access-Control-Allow-Origin头信息的HTTP回应) ==> (浏览器判断预检请求是否被允许) ==>(如果预检请求被通过,以后每次的非简单请求,就都和简单请求一样)
代码示例:
简单请求
var url = 'http://api.a.com/cors';// 在本站地址http://localhost:8080
var xhr = new XMLHttpRequest();
xhr.open('get',url,true);
xhr.send()
非简单请求
var url = 'http://api.a.com/cors';// 在本站地址http://localhost:8080
var xhr = new XMLHttpRequest();
xhr.open('put',url,true); //发起的非GET POST HEAD 的请求
xhr.send()
JSONP
利用HTML中script标签可以引入其他域下的js的特性,来实现跨域访问接口。后端配合返回如下格式的数据:
dosomething({
’aa‘:'bb',
‘cc’:{....}
})
因为是script标签的请求,所以返回的数据会作为javascript去执行。那么我们只用再定义一个dosomething函数就可以了,他的参数就是返回的json数据。
<script type="text/javascript">
function dosomething(json){
//处理JSONP放回的数据
}
</script>
<script src="http://api.a.com/cors?callback=dosomething"></script>
修改document.domain
在页面 http://b.example.com/a.html 中设置document.domain:
<div class='main' >
<input type="text">
</div>
<iframe src="http://a.example.com/b.html"></iframe>
<script type="text/javascript">
document.domain = 'example.com';//设置成主域
document.querySelector('.main input').addEventListener('input',function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;//对调用的iframe进行dom操作
})
</script>
在页面 http://b.example.com/b.html 中也设置document.domain:
<div class='main' >
<input id='input' type="text">
</div>
<script type="text/javascript">
document.domain = 'example.com';//设置成主域
document.querySelector('#input').addEventListener('input',function(){
window.parent.document.querySelector('input').value = this.value;//因为是用iframe调用了b.html所用用window.parent来获得父级页面
})
</script>
两个在同源下不同域的两个页面通过document.domain
可以实现主域名一样下两个子域名网站做iframe嵌套的时候进行相互操作dom。
PostMassage
window.postMessage() 方法可以安全地实现跨源通信。
示例
页面http://b.example.com/b.html 中设置PostMessage
<div class='main' >
<input type="text">
</div>
<iframe src="http://a.example.com/b.html"></iframe>
<script type="text/javascript">
document.querySelector('.main input').addEventListener('input',function(){
console.log(this.value);
window.frames[0].postMessage(this.value.'');// * 表示没有指定域名,也可以把*指定成固定的域名
})
window.addEventListener('message',function(e){ //监听message事件,可以监听别人给他发的内容,然后拿出来使用
document.querySelector('.main input').value = e.data
})
</script>
message事件还有如下属性:
-
data
从其他 window 中传递过来的对象。 -
origin
调用 postMessage时消息发送方窗口的 origin. 这个字符串由 协议、“://“、域名、“ : 端口号”拼接而成。例如 “https://example.org(implying port 443)”、“http://example.net(implying port 80)”、“http://example.com:8080”。请注意,这个origin不能保证是该窗口的当前或未来origin,因为postMessage被调用后可能被导航到不同的位置。 -
source
对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。
安全问题
如果你不希望从其他网站接收message,那么就不要为message事件添加任何事件侦听器。 这是一个完全万无一失的方式来避免安全问题。如果你用了,那么在监听message事件的时候一定要做origin的验证。如下:
function receiveMessage(event)
{ if (event.origin !== "http://example.org")
return;
}
window.addEventListener("message", receiveMessage, false);
这样你就可以保证你接受到的信息是来自你希望收到的网站
使用场景:
1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的iframe消息传递