1.同源策略如下:
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js |
同一域名下 | 允许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js |
同一域名下不同文件夹 | 允许 |
http://www.a.com:8000/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”。
浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。
2前端解决跨域问题
以下的例子包含的文件均为为 http://www.a.com/a.html
、http://www.a.com/proxy.html
与 http://www.b.com/b.html
,要做的都是从a.html
获取b.html
里的数据
一 JSONP
原理:jsonp
是利用script
标签没有跨域限制的特性,通过在src
的url
的参数上附加回调函数名字,然后服务器接收回调函数名字并返回一个包含数据的回调函数
通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
示例:
function doSomething(data) { // 对data处理 } var script = document.createElement("script"); script.src = "http://www.b.com/b.html?callback=doSomething"; document.body.appendChild(script); // 1.生成一个script标签,将其append在body上,向服务器发出请求 // 2.服务器根据 callback 这个参数生成一个包含数据的函数 doSomething({"a", "1"}) // 3.页面事先已声明doSomething函数,此时执行 doSomething(data) 这个函数,获得数据
二 window.name + iframe
原理:
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
window.name 的美妙之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
window.name
的原理是利用同一个窗口在不同的页面共用一个window.name
,这个需要在a.com
下建立一个代理文件proxy.html
,使同源后a.html
能获取proxy.html
的window.name
name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web 服务页面被加载后,必须导航 frame 回到原始域。同源策略依旧防止其他 frame 访问 name 属性。一旦 name 属性获得,销毁 frame 。
示例:
a.com/a.html
var iframe = document.createElement("iframe"); iframe.src = "http://www.b.com/b.html"; document.body.appendChild(iframe); // 现在a.html里建一个引用b.html的iframe,获得b的数据 var flag = true; iframe.onload = function() { if (flag) { iframe.src = "proxy.html"; // 判断是第一次载入的话,这时候iframe的window.name就含有所希望获得的数据,这时候设置iframe和a.html在同源,才能获取iframe的window.name的值,改变一个iframe的地址(src属性)重新加载后触发onload事件,第二次载入进入时flag为false进入else循环 flag = false; } else { // 第二次载入由于a和proxy同源,a可以直接获取proxy的window.name alert(iframe.contentWindow.name); iframe.contentWindow.close(); document.body.removeChild(iframe); iframe.src = ''; iframe = null; } }
b.com/b.html
window.name = "这是 b 页面的数据";
Web 服务器如何提供 window.name 数据
为了让 Web 服务器实现 window.name,服务器应该只寻找请求中是否包含 windowname 参数。如果包含了 windowname 参数,服务器应该返回一个设置了 window.name 字符串值的 HTML 文档,回应此请求并传送到客户端。例如:
http://www.a.com/getdata.html?windowname=true
如果服务器想用 Hello 响应客服端,它应该返回一个 HTML 页面:
<html>
<script type="text/javascript">
window.name="Hello";
</script>
</html>
同样也可以转换为 JSON 数据:
<html>
<script type="text/javascript">
window.name='{"foo":"bar"}';
</script>
</html>
三 document.domain(同属于同一个基础域名的两个域名之间可用)
原理:
把两个属于同一个基础域名的域名的 document.domain设置成自身或更高一级的父域,使得document.domain相同,这样通过在页面中书写js代码来获取iframe中的东西
浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。有一点需要说明,不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但蛋疼的是你却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器比如ie6也可以使用top、parent等少数几个属性),总之,你可以当做是只能获取到一个几乎无用的window对象。比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西
利用document.domain 实现跨域:
前提条件:这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域
有另一种情况,两个子域名:
aaa.xxx.com
bbb.xxx.com
aaa里的一个网页(a.html)引入了bbb 里的一个网页(b.html),这时a.html里同样是不能操作b.html里面的内容的。因为document.domain不一样,一个是aaa.xxx.com,另一个是bbb.xxx.com。这时我们就可以通过Javascript,将两个页面的domain改成一样的,需要在a.html里与b.html里都加入:document.domain = "xxx.com";这样这两个页面就可以互相操作了。也就是实现了同一基础域名之间的"跨域"。
示例:
a.com/a.html
document.domain = "a.com"; var iframe = document.createElement("iframe"); iframe.src = "http://b.a.com/b.html"; document.body.appendChild(iframe); iframe.onload = function() { console.log(iframe.contentWindow....); // 在这里操作b.html里的元素数据 这样我们就可以通过js访问到iframe中的各种属性和对象了。 }
b.a.com/b.html
document.domain = "a.com";
注意:document.domain
需要设置成自身或更高一级的父域,且主域必须相同。
不过如果你想在http://www.example.com/a.html 页面中通过ajax直接请求http://example.com/b.html 页面,即使你设置了相同的document.domain也还是不行的,所以修改document.domain的方法只适用于不同子域的框架间的交互。如果你想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。原理就是让这个iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据的,然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发送ajax请求,然后收到的数据我们也可以获得了。
四 HTML5的postMessage
原理:
假设在a.html
里嵌套个<iframe src="http://www.b.com/b.html" frameborder="0"></iframe>
,在这两个页面里互相通信
示例:
a.com/a.html
window.onload = function() { window.addEventListener("message", function(e) { alert(e.data); }); // window.frames[0].postMessage("b data", "http://www.b.com/b.html"); }
b.com/b.html
window.onload = function() { // window.addEventListener("message", function(e) { // alert(e.data); // }); window.parent.postMessage("a需要的数据", "http://www.a.com/a.html"); }
五 CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)克服了AJAX只能同源使用的限制
CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
CORS
是XMLHttpRequest Level 2
里规定的一种跨域方式。在支持这个方式的浏览器里,javascript
的写法和不跨域的ajax
写法一模一样,
只要服务器需要设置Access-Control-Allow-Origin: * 或者请求头中Origin的源信息
(ajax跨域请求时,会自动在请求头部添加一个 Origin: http://www.ccd.com 协议 域名 端口 以便于服务器根据这个头部信息来决定是否给予响应,所以关键在于服务器的设置)
如果没有这个头部,或者有这个头部但是源信息不匹配,浏览器就驳回请求
六 websocket
web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)
web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。
只有在支持web socket协议的服务器上才能正常工作。
var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss socket.send('hello WebSockt'); socket.onmessage = function(event){ var data = event.data; }
七 window.location.hash + iframe
原理:
b.html
将数据以hash
值的方式附加到proxy.html
的url
上,在proxy.html
页面通过location.hash
获取数据后传到a.html
(这个例子是传到a.html
的hash
上,当然也可以传到其他地方)
示例:
a.com/a.html
var iframe = document.createElement("iframe");
iframe.src = "http://www.b.com/b.html";
document.body.appendChild(iframe); // 在a页面引用b
function check() { // 设置个定时器不断监控hash的变化,hash一变说明数据传过来了
var hashs = window.location.hash;
if (hashs) {
clearInterval(time);
alert(hashs.substring(1));
}
}
var time = setInterval(check, 30);
b.com/b.html
window.onload = function() {
var data = "this is b's data";
var iframe = document.createElement("iframe");
iframe.src = "http://www.a.com/proxy.html#" + data;
document.body.appendChild(iframe); // 将数据附加在proxy.html的hash上
}
a.com/proxy.html
// 获取自身的hash再传到a.html的hash里,数据传输完毕 parent.parent.location.hash = self.location.hash.substring(1);
八 通过script标签加载
对于浏览器来说,script标签的src属性所指向资源就跟img标签的src属性所指向的资源一样,都是一个静态资源,浏览器会在适当的时候自 动去加 载这些资源,而不会出现所谓的跨域问题。这样我们就可以通过该属性将要访问的数据对象引用进当前页面而绕过js跨域问题。 例如,需要在 hi域 下管理中心页面中随机推荐几个热门模块给用户,由于热门模块的相关信息都在 act域 下的php模块中维 护,如果直接在hi域下通过ajax请求去获取act域下的推荐模块列表相关信息就出现js跨域问题。解决这个问题的最简单方法就是,在hi域下通过 script标签去访问act域提供的这个http接口:
<script type=”text/javascript” src=”http://act.hi.baidu.com/widget/recommend”><script>
参考:https://segmentfault.com/a/1190000003882126
http://www.cnblogs.com/JChen666/p/3399951.html