参考:轻松搞定JSONP跨域请求
参考:JavaScript 跨域总结与解决办法
要理解跨域,先要了解一下“同源策略”。
所谓同源是指,域名,协议,端口相同。
简单的说就是基于安全考虑,当前域不能访问其他域的东西。
什么是跨域
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。
网上一张列举跨域的详细清单:
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js
|
同一域名下 | 允许 |
http://www.a.com/lib/a.js http://www.a.com/script/b.js
|
同一域名下不同文件夹 | 允许 |
http://www.a.com:8080/a.js http://www.a.com/b.js
|
同一域名,不同端口 | 不允许 |
http://www.a.com/a.js https://www.a.com/b.js
|
同一域名,不同协议 | 不允许 |
http://www.a.com/a.js http://70.32.92.74/b.js
|
域名和域名对应IP | 不允许 |
http://www.a.com/a.js http://script.a.com/b.js
|
主域相同,子域不同 | 不允许 |
http://www.a.com/a.js http://a.com/b.js
|
同一域名,不同二级域名 (同上) |
不允许 (cookie这种情况下也不允许访问) |
http://www.cnblogs.com/a.js http://www.a.com/b.js
|
不同域名 | 不允许 |
有两点要特别注意:
- 如果是协议和端口造成的跨域问题“前台”是无能为力的
- 在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
- “URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
拿豆瓣的api简单尝试一下跨域请求出现的问题。
window.onload = function(){
var oBtn = document.getElementById('btn');
oBtn.onclick = function(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
}
}
// No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
xhr.open('get','https://api.douban.com/v2/book/search?q=javascript&count=1',true);
xhr.send();
}
}
浏览器会报出上面的错误 ,请求资源上不存在“访问控制允许源”标头。
JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写
我们知道,<img>
的src(获取图片),<link>
的href(获取css),<script>
的src(获取javascript)这三个都不符合同源策略,它们可以跨域获取数据。
这里要介绍的JSONP就是利用 <script>
的src来实现跨域获取数据的。
实现跨域的原理就是动态生成<script>
标签,然后利用<script>
的src不受同源策略约束来跨域获取数据。
function handleResponse(response){
console.log(response);
}
window.onload = function(){
var oBtn = document.getElementById('btn');
oBtn.onclick = function(){
// 动态生成script标签,将请求路径赋给src,然后放到文档中
var script = document.createElement('script');
script.src = 'https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse';
document.body.insertBefore(script,document.body.firstChild);
}
}
使用jquery的封装好的jsonp来实现跨域
$.ajax({
async: true,
url: 'https://api.douban.com/v2/book/search',
type: 'GET',
dataType: 'jsonp', //返回的数据类型,设置为jsonp方式
jsonp: 'callback', // 指定一个查询参数名称来覆盖默认的jsonp回调参数 callback
jsonpCallback: 'handleResponse', // 设置回调函数名
data: {
q: 'javascript',
count: 1
},
success: function(response,status,xhr){
console.log('状态为:'+status+'状态是:'+xhr.statusText);
console.log(response);
}
})
使用$.getJSON来实现跨域
$.getJSON('https://api.douban.com/v2/book/search?callback=?',{
q: 'javascript',
count: 1
},function(response,status,xhr){
console.log('状态为:'+status+'状态是:'+xhr.statusText);
console.log(response);
})
document.domain+iframe的设置
对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。 具体的做法是可以在http://www.a.com/a.html
和http://script.a.com/b.html
两个文件中分别加上 document.domain = ‘a.com’
;然后通过a.html
文件中创建一个iframe
,去控制iframe
的contentDocument
,这样两个js文件之间就可以 “交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,如果你异想天开的把script.a.com的domian设为alibaba.com 那显然是会报错地!代码如下:
// www.a.com上的a.html
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在这里操纵b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
// script.a.com上的b.html
document.domain = 'a.com';
这种方式适用于www.kuqin.com
, kuqin.com
, script.kuqin.com
, css.kuqin.com
中的任何页面相互通信。
备注:
问题:
- 安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
- 如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。