浏览器同源策略
浏览器出于安全考虑,默认情况下,只允许在本域接口下进行数据交互。这是浏览器的一种安全保护机制。主要限制有两个方面:
一、不同源的文档不能通过ajax实现请求,例如通过XHR实现Ajax通信,默认,XHR只能访问与包含它的页面位于同一个域中的资源,而不能访问其他域下的资源。
二、浏览器中不同域的框架也是不能通过js进行交互操作。
什么是跨域?
只要协议、域名、端口号中有任何一个不相同,就算是不同的域。要实现在不同域之间进行数据交互,就需要用到跨域的技术。
对于端口号和协议不同的情况,只能通过后台实现跨域。
跨域的几种方式:
1、CORS(跨域资源共享Cross-Origin Resource Sharing)
定义了在必须跨域访问的情况下,服务器和浏览器是如何进行沟通。cors背后的思想就是通过自定义的HTP头部的信息的反馈,决定整个请求响应过程的成败。
比较一下在使用cors实现跨域和没有跨域的情况下,ajax代码的区别:
var xhr=new XMLHttpRequest();
xhr.open('get','/loadMore?index='+pageIndex+'&length=5',true) // 没有cors url是一个相对路径
xhr.send();
var xhr=new XMLHttpRequest();
xhr.open('get', 'http://baidu.com:8080/getNews', true); // cors 实现跨域 URL换成其他域的绝对地址
xhr.send();
如上使用get方法发送一个请求时,在他的头部附加一个Origin头部,其中包含请求页面的源信息(协议、域名、端口)
Origin: http://baidu.com:8080
服务器根据这个头部信息来决定是否允许响应,如果服务器认为这个请求可以接受,就会在Access-Control-Allow-Origin的头部中回发同样的源信息
Access-Control-Allow-Origin: http://baidu.com:8080
如果没有这个头部,或者源信息不匹配,浏览器就会驳回请求。
2、JSONP
(JSON with padding)是JSON的一种使用格式,可以让网页访问其他网域下的资源,也叫填充式JSON
JSONP的特性:
html中的script标签可以引用其他域中的文件,可实现跨域访问。需要后端的支持与配合。
JSONP由两部分组成:回调函数和数据;
回调函数就是在响应到来的时候,在页面中调用的函数,数据就是服务器发过来的json数据
callback({"name":"sty"})
使用jsonp来实现跨域:
js文件
var script=document.createElement('script'); //创建script元素
script.src='http://a.jrg.com:8080/getNews?callback=appendHtml'; //指定要访问的URL,其中回调函数是appendChild,用于处理数据
document.head.appendChild(script); //在页面头部添加script节点
document.head.removeChild(script); //在实现跨域功能后,移除script节点,保持页面的简洁
后端:
var cb = req.query.callback;
if(cb) {
res.send(cb+'('+JSON.stringify(data)+')');
} else {
res.send(data);
}
JSONP的优缺点
JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
CORS和JSONP对比
1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。
使用JSONP实现跨域,也会存在一定的安全隐患,例如XSS攻击
3、降域
浏览器中不同域的框架也是不能通过js进行交互的,但是不同框架之间可以获取到window对象,但却无法获取到相应的属性和方法。
例如 a.baidu.com 域下的一个 html 文档里有一个在其他域下(b.baidu.com)的 iframe 框架,在a.baidu.com 中并不能访问到 b.baidu.com 里的数据;
但可以获取到 b.baidu.com 中的 window 对象,但是这时 window 的属性和方法并不可用,两个文件中使用 document.domain('baidu.com')方法 把域名都降到baidu.com;
这样就可以在 a.baidu.com 中使用 iframe 里面的 window 的所有属性和方法了,通过window.frames[0] 获取到 iframe 框架,但是这时再通过window.frames[0].document.querySelector(element) 获取到 iframe 里的 element 元素。
在b.baidu.com 中通过window.parent.document.querySelector(element) 获取到html里的元素。
document.domain只适用于不同子域的框架之间实现跨域访问。
主要使用document.domain()
4、postMessage
这是HTML5的一种跨域访问资源的方法。
window.postMessage(message,targetOrigin)方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
document.querySelector('.main input').addEventListener('input',function() {
console.log(this.value);
window.frames[0].postMessage(this.value,"*") //访问不同源的框架的message
})
window.addEventListener('message',function(e) { /*在window上绑定一个监听事件,监测message*/
document.querySelector('.main input').value=e.data//通过e.data可以监听到别人发给我的信息
console.log(e.data);
})