JS 跨域详解
文章目录
跨域与同源
跨域
跨域是指从一个域名的网页去请求另一个域名的资源。比如从 www.baidu.com
页面去请求 www.google.com
的资源。
非同源,在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。
同源策略
同源策略是浏览器最核心也最基本的安全功能。如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
- 同源:协议、域名、端口,三者全部相同,才是同源。
- 跨域:协议、域名、端口,只要有一个的不同,就是跨域。
http://www.123.com/index.html
调用 http://www.123.com/server.php
(非跨域)
http://www.123.com/index.html
调用 http://www.456.com/server.php
(主域名不同:123/456,跨域)
http://abc.123.com/index.html
调用 http://def.123.com/server.php
(子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html
调用 http://www.123.com:8081/server.php
(端口不同:8080/8081,跨域)
http://www.123.com/index.html
调用 https://www.123.com/server.php
(协议不同:http/https,跨域)
请注意:localhost
和 127.0.0.1
虽然都指向本机,但也属于跨域。
不存在跨域的情况(无视同源策略)
- 服务端请求服务端不存在跨域(浏览器请求服务器才存在同源策略)
<img src="跨域的图片地址">
(<img>
标签的src
属性不存在跨域)<link href="跨域的css地址">
(<link>
标签的href
属性不存在跨域)<script src="跨域的js地址"></script>
(<script>
标签的src
属性不存在跨域)
跨域常见的几种方法
1. jsonp 跨域
jsonp是一种跨域通信的手段,它的原理其实很简单:
利用<script>
标签的src属性跨域
由于使用script标签的src属性,因此只支持get方法
function jsonp(req){
// 动态创建script标签
var script = document.createElement('script');
var url = req.url + '?callback=' + req.callback.name;
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
// 调用
jsonp({
url : '',
callback : hello
});
当然,jquery也支持jsonp的实现方式:
$.ajax({
url:'http://www.xxx.com',
type:'GET',
dataType:'jsonp',// 请求方式为jsonp
jsonpCallback:'callback',
data:{
"username":"xxx"
}
})
2. document.domain + iframe 跨域
两个域名必须属于同一个主域名!
而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域。
什么是主域名相同呢?
- www.baidu.com(百度官网)
- map.baidu.com(百度地图)
- tieba.baidu.com(百度贴吧)
- news.baidu.com(百度新闻)
- image.baidu.com(百度图片)
这五个主域名都是baidu.com
.
举个例子,现在有两个页面:
- news.baidu.com(news.html)
- map.baidu.com(map.html)
news.baidu.com里的一个网页(news.html)引入了map.baidu.com里的一个网页(map.html)
这时news.html里同样是不能操作map.html里面的内容的。
因为document.domain不一样,一个是news.baidu.com,另一个是map.baidu.com。
这时我们就可以通过Javascript,将两个页面的domain改成一样的
,
需要在a.html里与b.html里都加入:
document.domain = “baidu.com”;
这样这两个页面就可以互相操作了。也就是实现了同一一级域名之间的"跨域"。
具体代码:
<!-- news.baidu.com下的news.html页面 -->
<script>
document.domain = 'baidu.com';
var ifr = document.createElement('iframe');
ifr.src = 'map.baidu.com/map.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 这里可以跨域操作map.baidu.com下的map.html页面
var oUl = doc.getElementById('ul1');
alert(oUl.innerHTML);
ifr.onload = null;
};
</script>
<!-- map.baidu.com下的map.html页面 -->
<ul id="ul1">我是map.baidu.com中的ul</ul>
<script>
document.domain = 'baidu.com';
</script>
3. postMessage跨域
在HTML5中新增了postMessage方法,postMessage可以实现跨文档消息传输(Cross Document Messaging)
Internet Explorer 8, Firefox 3, Opera 9, Chrome 3和 Safari 4都支持postMessage
postMessage可以解决:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
postMessage(data,origin)方法接受两个参数:
- data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
- origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
.
举个栗子
a.html (www.baidu.com/a.html)
<iframe id="iframe" src="http://www.qq.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'xxx'
};
// 向qq.com传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.qq.com');
};
// 接受由qq.com返回数据
window.addEventListener('message', function(e) {
alert('data from qq ---> ' + e.data);
}, false);
</script>
b.html (www.qq.com/b.html)
<script>
// 接收由baidu.com返回的数据
window.addEventListener('message', function(e) {
alert('data from baidu ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 处理后再发回baidu.com
window.parent.postMessage(JSON.stringify(data), 'http://www.baidu.com');
}
}, false);
</script>
4. CORS 跨域资源共享
是目前主流的跨域解决方案
5. node代理跨域
跨域行为是浏览器禁止的,但是服务端并不禁止。
使用proxyTable的原理就是将域名发送给本地的node服务器,再由本地的服务器去请求真正的服务器。
以vue为例
config/index.js文件中
proxyTable: {
'/api': {
// 测试环境
target: 'http://baidu.com', // 接口域名 要代理的url
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api': ''
}
}
配置完必须要重启node服务才会生效!!!