跨域与同源策略
浏览器同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
这是一个用于隔离潜在恶意文件的关键的安全机制。如果协议、端口、主机是相同的,则两个页面源相同。下表是相对于http://abc.com/同源检测
URL | 是否同域 | 原因 |
---|---|---|
http://abc.com/a/ | 是 | 协议、端口、主机相同 |
https://abc.com/ | 否 | 不同的协议( https ) |
http://abc.com:81/ | 否 | 不同的端口 ( 81 ),默认端口为80 |
http://newabc.com/ | 否 | 不同的主机 ( news ) |
http://www.abc.com/ | 否 | 域名不同,顶级域名与www子域名不是一个概念 |
postMessage(HTML5)
语法
otherWindow.postMessage(message, targetOrigin, [transfer]);
// otherWindow 发送窗口,比如iframe的contentWindow
// message 发送信息
// targetOrigin 定哪些窗口能接收到消息事件 "*" 表示无限制
// transfer [可选] 是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
发送端设置Origin
在使用postMessage
时需要注意的是,如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
例子:3000端口页面向3001端口页面postMessage
,配置targetOrigin
为http://192.168.31.235:3001。
<iframe id="iframe" src="http://192.168.31.235:3001/test"></iframe>
<script type="text/javascript">
function postMsg(){
var msg = {psw: '123456'};
var targetOrigin = "http://192.168.31.235:3001";
var frame = document.getElementById('iframe');
frame.contentWindow.postMessage(msg, targetOrigin);
}
</script>
如果postMessage
发送的目标不是配置的targetOrigin
,浏览器就会报错,例如targetOrigin配置的是http://192.168.31.235:3001
,而iframe
的域是http://192.168.31.235:3002
。
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://192.168.31.235:3002') does not match the recipient window's origin ('http://192.168.31.235:3001').
接收端检测Origin
为了防止恶意站点发送postMessage
,接收端同样需要检测Origin
。
3001端口(与3000跨域)接受来自3000端口的postMessage
,判断是否是目标源,如果是继续工作,否则不做处理。
window.addEventListener("message", onReceivedMessage, false);
function onReceivedMessage(event){
var origin = event.origin || event.originalEvent.origin;
if(origin === 'http://192.168.31.235:3000'){
// do something
}else{
return;
}
}
Access-Control-Allow-Origin 授权
通过目标域返回的HTTP头(
Access-Control-Allow-Origin
)来授权是否允许跨域访问,默认情况下不允许跨域访问。
语法
允许所有站点请求
Access-Control-Allow-Origin: *
允许特定站点请求
Access-Control-Allow-Origin: http://192.168.31.235:3001
浏览器支持
流程图
Access-Control-Allow-Origin 跨域请求流程图
例子
现在有2个域,端口3000和3001,3000端口页面ajax请求3001端口的服务,毫无疑问,会跨域报错。
$.ajax({
url:'http://192.168.31.235:3001/doSomething',
data:{},
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
success: function(){},
error: function(){}
});
现在3001 doSomething接口设置Access-Control-Allow-Origin
(以express为例)。
res.header("Access-Control-Allow-Origin", "*");
res.send({
status: '200',
msg: 'ok'
});
继续请求ajax请求,跨域请求成功。
设置*
最大的问题是不判断请求来源,意味着所有请求都可以访问。
解决方法是Access-Control-Allow-Origin
授权给特定站点,只有特定站点才可访问,避免非授权访问。
res.header("Access-Control-Allow-Origin", "http://192.168.31.235:3000");
如果响应头 Access-Control-Allow-Origin
设置成http://192.168.31.235:3002
域才可以访问接口,继续原来的请求就会跨域报错。
XMLHttpRequest cannot load http://192.168.31.235:3001/doSomething. The 'Access-Control-Allow-Origin' header has a value 'http://192.168.31.235:3002' that is not equal to the supplied origin. Origin 'http://192.168.31.235:3000' is therefore not allowed access.