//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
window.name + iframe
window.name (一般在js代码里出现)的值不是一个普通的全局变量,而是当前窗口的名字,这里要注意的是每个iframe都有包裹它的window,而这个window是top window的子窗口,而它自然也有window.name的属性, window.name属性的神奇之处在于name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。
window.name = 'abc'
window.name
window.location = 'http://www.baidu.com'
window.name
复制代码
a.html, proxy.html 同属于一个域下,b.html是另一个域的
b.html内容
<script>
window.name = 'b.html\'s data';
</script>
复制代码
a.html
<script type="text/javascript">
var otherLoaded = false,
iframe = document.createElement('iframe'),
loadfn = function() {
if (otherLoaded) {
var data = iframe.contentWindow.name
alert(data)
// 清理工作
iframe.contentWindow.document.write('')
iframe.contentWindow.close()
document.body.removeChild(iframe)
} else if (!otherLoaded) {
otherLoaded = true
// 设置的代理文件
iframe.contentWindow.location = "http://localhost:8001/proxy.html"
iframe.src = 'http://localhost:8002/b.html'
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn)
} else {
iframe.onload = loadfn
document.body.appendChild(iframe)
</script>
复制代码
可以看到, 第一次设置iframe的地址为b.html, 这样的话b.html会被加载进来,但是并不能直接访问iframe.contentWindow.name, 因为a.html和b.html目前不同源,如果将loadfn的实现改为var data = iframe.contentWindow.name;
,会出来这个错误:
a.html:
Uncaught DOMException: Blocked a frame with origin "http:
复制代码
那怎么办呢, 既然不同源, 就改成同源呗, 所以将iframe地址改成与a.html同源的proxy.html,由于window.name在地址变化时值不变, 所以iframe.contentWindow.name的值还是之前的值, 也就是b.html窗口的值, 而又满足的同源的要求, 所以可以访问成功。
postMessage
window.postMessage
的功能是允许程序员跨域在两个窗口/frames间发送数据信息。基本上,它就像是跨域的AJAX,但不是浏览器跟服务器之间交互,而是在两个客户端之间通信。安全着想,接受消息的时候应该检验来源,也就是event.origin或者event.source。
a.html
<h1 class="header">page A</h1>
<div class="mb20">
<textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
<button style="font-size:20px;" onclick="send()">post message</button>
</div>
<iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>
<script>
function send() {
var data = document.querySelector('#data').value;
window.frames[0].postMessage(data, 'http://localhost:9022/');
window.addEventListener('message', function(messageEvent) {
if (messageEvent.origin !== 'http://localhost:9022/') return
var data = messageEvent.data;
console.info('message from child:', data);
}, false);
</script>
复制代码
b.html
<h1 class="header">page B</h1>
<input type="text" id="inp" value="some contents..">
<button onclick="send()">send</button>
<script>
window.addEventListener('message', function(ev) {
if (ev.source !== window.parent) {return;}
var data = ev.data;
console.info('message from parent:', data);
}, false);
function send() {
var data = document.querySelector('#inp').value;
window.parent.postMessage(data, 'http://localhost:9011/');
</script>
跨域资源共享(CORS)
CORS 通过新增一系列的HTTP头,让服务器能声明那些来源能访问该服务器上的资源,GET以外的请求,会以OPTIONS请求方式发一个预请求,从而得知服务器对资源请求支持的HTTP方法,在确认服务器允许跨域请求资源的情况下,以实际的HTTP请求方法发送真正的请求。
Origin
:
普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域。
Access-Control-Request-Method
:
接下来请求的方法,例如PUT, DELETE等等
Access-Control-Request-Headers
:
自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中
http响应头
然后浏览器再根据服务器的返回值判断是否发送非简单请求。简单请求前面讲过是直接发送,只是多加一个origin字段表明跨域请求的来源。然后服务器处理完请求之后,会再返回结果中加上如下控制字段
Access-Control-Allow-Origin
:
允许跨域访问的域,可以是一个域的列表,也可以是通配符"*"。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://foo.example/subdir/ 是无效的。但是不同子域名需要分开设置,这里的规则可以参照同源策略
Access-Control-Allow-Credentials
:
是否允许请求带有验证信息,XMLHttpRequest请求的withCredentials标志设置为true时,认证通过,浏览器才将数据给脚本程序。
Access-Control-Expose-Headers
:
允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息
Access-Control-Max-Age
:
缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数
Access-Control-Allow-Methods
:
允许使用的请求方法,以逗号隔开
Access-Control-Allow-Headers
:
自定义的头部,以逗号隔开,大小写不敏感