iframe跨域通讯解决方案
需求: 网站B内嵌套网站A的功能, 网站A有自己的登陆系统, 网站B也有自己的登陆系统, 要求, 网站B内打开A的里面的功能时, 免登陆
分析: 刚开始听到这个需求, 这个和我应该没有太大关系, 后端兼容一下token, 我稍微支持一下应该就ok了吧
实际: 后端说兼容不了token, 两边登陆系统不一样, 所以决定由前端, 根据打开的url来做处理
于是本着不懂就要学的精神开始了我的探索(以下讨论属于跨域的情况下).
方案1: 不需要传值, iframe拿到外层的host来进行判断.拿到host的方法如下
var url = null;
if (parent !== window) {
try {
url = parent.location.href;
} catch (e) {
url = document.referrer;
}
}
return url;
在拿到外层url的时候还有一个系统的方法, 不过不是所有的浏览器都支持的, location.ancestorOrigins;可以直接获取到外层的host,
火狐和fireforx和internetExplorer没有浏览器没有去找合适的方法, 有小伙伴告知, 感激不尽.....
评价: 拿到外层的host之后, 就是一些逻辑处理, 这个样子好吗? 根据host来判断感觉比较危险, 代码中写了一大片的逻辑, 后来者维护的时候可能不知道这个是要嵌入到别的网站的页面(必要的备注, 也不一定会被对方留意到), host以后改变了呢? 如果从外面传参过来会不会好一点? 废话少说, 先实践一波, 毕竟我爱学习
我爱学习方案2: 从外层传值给iframe
1.postMessage API说明
The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.
Normally, scripts on different pages are allowed to access each other if and only if the pages they originate from share the same protocol, port number, and host (also known as the "same-origin policy"). window.postMessage() provides a controlled mechanism to securely circumvent this restriction (if used properly). (顺带学点英语)
简单方便, 可以解决 页面和其打开的新窗口的数据传递 ,多窗口之间消息传递, 页面与嵌套的iframe消息传递的跨域问题
demo必要代码如下:
B页面:
<iframe id="iframe_ID" src="http://localhost:8082/#/home" class="iframe"></iframe>
var iframe = document.getElementById("iframe_ID");
iframe.onload = function() {
var data = {
name: '孩子, 我来看你了'
}
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://localhost:8082/#/home');
}
A页面:
const receiveMessage = (event) => {
console.log(event.data);//孩子, 我来看你了
};
window.addEventListener('message', receiveMessage, false);
接收到B页面的信息
当然也可以实现A页面向B页面传值:window.parent.postMessage({haha:'最近还好吗'}, 'http://localhost:8081');
2.在A页面加参数, B页面获取到; 根源https://developer.mozilla.org/en-US/docs/Web/API/Location/search
The search property of the Location interface is a search string, also called a query string; that is, a USVString containing a '?' followed by the parameters of the URL.
可以获取到?后面的参数var data = location.search.split("=")[1];获取第一个参数
单向传值直接这一个api可以搞定;
A页面 B页面嵌入的A页面改为http://a.com?data='xxx'
var data = location.search.split("=")[1];//data=。。。
data = decodeURI(data);
console.log('---来自父组件的值', data);
如果实现双向传值, 可以用代理的方式
代码如下:A页面http://localhost:8082
var data = location.search.split("=")[1];//data=。。。
//如果有中文
data = decodeURI(data);
//使用window.localStorage存值,主页面可以调用事件获取该值,也可以用其他办法。
// window.localStorage.setItem("data",data);
console.log('孩子, 你带了什么给爸爸', data);
window.onload = function(){
var mainValue = "这是父页面的值";
/*
* 2.传值给跨域的iframe
* 首先建立代理
*/
var proxyOther = document.createElement('iframe');
proxyOther.name = 'proxyOtherDomain';
proxyOther.style.display = 'none';
document.body.appendChild(proxyOther);
//这一步是关键,当需要传其他值时可以改变这个src
proxyOther.src = 'http://localhost:8081/index.html?data='+mainValue;
}
B页面http://localhost:8081
var data = location.search.split("=")[1];//data=。。。
//如果有中文
data = decodeURI(data);
console.log('---来自父组件的值', data);
window.onload = function() {
var otherValue = "这是子页面的值";
//首先建立代理
var proxyMain = document.createElement('iframe');
proxyMain.name = 'proxyMain';
proxyMain.style.display = 'none';
document.body.appendChild(proxyMain);
/*
* 1.传值
* 值可能是经过处理动态生成的,所以用setTimeout模拟一下
*/
// setTimeout(function(){
proxyMain.src = 'http://localhost:8082/index.html?data='+otherValue;
// },2000);
/*
* 2.获取父页面传来的值
*/
window.onstorage = function(){
var data = window.localStorage.getItem("data");
alert(data);
window.localStorage.clear();
}
// 也可以直接获取
setTimeout(function(){
console.log(window.parent.proxyOtherDomain.data)
},3000);
}
3.使用window.name+iframe
代码如下: A页面:http://localhost:8081
// 加载跨域页面
var iframe = document.createElement('iframe');
iframe.src = "http://localhost:8082/#/home";
let count = 0;
// onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
iframe.onload = function() {
// 第1次onload(跨域页)成功后,切换到同域代理页面
console.log('-----', count);
if(count === 0) {
console.log('-----');
iframe.contentWindow.location = 'http://localhost:8081/proxyOtherDomain.html';
count = 1;
} else {
console.log(iframe.contentWindow.name);
}
};
document.body.appendChild(iframe);
B页面:http://localhost:8082
var data ={
name:"fangdown",
age:30
}
window.name =JSON.stringify(data);
这个我测试的时候是获取到iframe里面的window.name, 原理是, 在打开iframe的时候, 巧妙的利用了iframe.src和iframe.contentWindow.location, 拿到了window.name.
后续, 采用了第一种方式之后, 调试的时候, 惊喜的发现, 他网站的session, 放到我页面session上面, 竟然是可以发出请求的, 原来token本来就是通的, 那就采用把token传过来的方式来解决这个免密登陆的问题