同源策略(Same origin policy)
同源策略是由 Netscape 提出的一个著名的安全策略。现在所有支持
JavaScript 的浏览器都会使用这个策略。所谓同源指域名,协议,端口都相同。浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
跨域
跨域是指从一个域名的网页去请求另一个域名的资源。只要协议、域名、端口有任何一个不同,都被当作是不同的域。比如从http://www.baidu.com/
页面去请求 http://www.google.com
的资源。
跨域的方法
-
通过 jsonp 跨域
html 中 script 标签可以引入其他域下的 js,比如引入线上的 js 库等。JSONP 就是利用这一点,通过 script 标签实现跨域请求,然后在服务端输出 JSON 数据并执行回调函数,从而解决了跨域的数据请求。
例:
<script>
function callback(result) {
console.log(result);
}
var jsonp=document.createElement("script");
jsonp.src="http://example.com/data.php?callback=callback";
document.getElementsByTagName("head")[0].appendChild(jsonp);
</script>
// 或者
<script>
function callback(result) {
console.log(result);
}
</script>
<script src="http://example.com/data.php?callback=callback"></script>
// 获取数据的地址后面还有一个 callback 参数,按惯例是用这个参数名,但是用其他的也一样。只要提前和后端沟通好就 OK 啦~
JSONP 不受同源策略的限制;它的兼容性更好,在古老的浏览器中都可以运行,不需要 XMLHttpRequest 或 ActiveX 的支持;在请求完毕后可以通过调用 callback 的方式回传结果。但它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;并且只能支持跨域 HTTP 请求,不能解决不同域的两个页面之间进行 JavaScript 调用的问题。
-
通过 CORS 跨域
CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。 实现方式很简单,当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。因此,只要服务器实现了 CORS 接口,就可以跨域通信。
例:
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com/data/", true);
// url 为跨域的绝对地址
xhr.send();
</script>
// 服务器端对于 CORS 的支持,主要就是通过设置 Access-Control-Allow-Origin 来实现。如果浏览器检测到相应的设置,就允许 Ajax 进行跨域访问。
相对于 JSONP 来说 CORS 更为强大,它支持所有类型的HTTP请求。使用CORS,开发者可以使用普通的 XMLHttpRequest 发起请求和获得数据。但 CORS 的兼容性远不如 JSONP,许多古老的浏览器都不支持 CORS。
-
通过降域(document.domain)实现跨域
不同的框架之间可以获取 window 对象,但无法获取相应的属性和方法。例如,有个页面,地址是http://www.example.com/a.html
, 在这个页面里面有一个 iframe,它的 src 为http://example.com/b.html
, 显然这个页面与它的 iframe 框架是不同域的,所以我们是无法通过在页面中书写 js
代码来获取 iframe 中的东西的。
此时,通过降域就可以实现该页面和 iframe 框架的交互。只需要将这两个页面的document.domain
属性都设置成http://example.com
就可以实现降域。但要注意的是,document.domain
的设置是有限制的,我们只能把document.domain
设置成自身或更高一级的父域,且主域必须相同。
因此,修改document.domain的方法只适用于不同子域的框架间的交互。 -
通过 postMessage 方法跨域
这个功能主要包括接受信息的message
事件和发送消息的
postMessage
方法。比如baidu.com
的页面1通过 iframe 嵌入了一个google.com
的页面2,可以通过 postMessage 实现1和2通信。
// 页面1通过 postMessage 方法发送消息
window.onload = function() {
var targetOrigin = "http://google.com";
node.contentWindow.postMessage('hello world!', targetOrigin);
};
postMessage 用法:
postMessage(data,origin);
data:要传递的数据,html5 规范中提到该参数可以是 JavaScript 的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用
JSON.stringify()
方法对对象参数序列化。
origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()
方法只会将 message 传递给指定窗口,当然如果愿意也可以建参数设置为*
,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为/
。
// 页面2通过 message 事件监听并接受消息
var onmessage = function (event) {
var data = event.data; // 消息
var origin = event.origin; // 消息来源地址
if(origin == "http://baidu.com") {
console.log(data);
}
};
-
通过 location.hash 跨域
location.hash
指 URL 中#
及其后面的字符。它一般用于浏览器锚点定位,Server 端并不关心这部分,应该说 HTTP 请求过程中不会携带 hash,所以这部分的修改不会产生 HTTP 请求,但是会产生浏览器历史记录。此方法的原理就是改变 URL 的 hash 部分来进行双向通信。 -
通过 window.name 跨域
window.name
属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name
的,每个页面对window.name
都有读写的权限,window.name
是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
【注】版权归 Lucifer 所有,转载请联系作者。
网友评论