页面跨域解决
场景:
这次项目遇到一个问题就是希望编辑后马上体现,我们这的站点域名是不能重复的。早期设计的是2个ifream,分左右,左边是编辑区,右边是新站点展现。遇到的问题就是新站点的ip因为跟本站点不一样,所会出现跨域。
左边是编辑区,通过点击右边的栏目展现。
右边是新站点页面,ip与当前新增站点的ip不一致。
一、布局设置
左右两边是ifream,父页面用的fream框架,分左右。
<frameset cols="45%,*" frameborder="0" border="0" framespacing="0">
<frame src="" name="leftFrame" noresize="noresize" id="leftFrame" class="cbody" />
<frame src="" name="channelRightFrame" id="rightFrame" scrolling="auto"/>
<noframes>
<body>您的浏览器版本太低,无法处理框架</body>
</noframes>
</frameset>
二、期望效果
左右2边可以互相操作,实现左边编辑保存,右边刷新。右边点击,左边显示对应信息,提供保存。
如果是同域,肯定大家都知道把相关方法写在父页面上,然后用window.parent.xxxjs()的方式实现左右页面刷新。
结果调用报错:Access-Control-Allow-Origin
现在因为我当前域是localhost:8080,右边的域是我本机ip,这样肯定就是2个域了。
这里就扩展问大家一个问题吧,localhost:8080与127.0.0.1:8080是否是同一个域?
三、实践过的思路
window.parent不行报错后,我自己的想法如下:
1、最初考虑是用全局参数,我在子页面往父页面存一个值,父页面加定时器读取,读取到后就执行想方法,验证发现根本不允许往父页面存值。
2、以前使用H5的storage完成过一个流程化的操作,跳页面了依然可以在storage里取到值,就想到利用这一特性,这次无论是localStorage还是sessionStorage,都没能穿透,虽然不报错,但是取不到值,值在当前域的页面里调用取值能取到。
到这里我就慌了,首先本来时间有点紧,大家都事还挺多,这个跨域也是之前自己没有预估到的,如果这个解决不了,那就是整个设计都有问题啊。于是马上问队友,领导说可以用nginx服务器,为了这个就上台服务器成本太大了,我也不会nginx,时间成本太高直接pass,队友说下个谷歌插件Allow-Control-Allow-Origin: *,验证发现依然不行,详细了解,他们的情况是接口跨域,我是页面,还有点不一样,这种下插件体验也差,于是我也放弃了。
然后开始百度大法,有提到用windows.hash,window.name实现的,于是思路还是先在新域页面调用往name或hash存值,父页面定时器读取,读到值做相应操作,结果我使用的时候没有吃透,它是需要一个ifream作为中间人的,而我没有设置,直接在老域就window.hash所以没有渠道值。我新域的页面肯定是不能做任何改动的(调用js除外),所以继续百度。
终于发现了CSDN坛友的一片文章,有demo,下载还需要金币,感觉靠谱。于是登陆我自己的CSDN号下载(我也是VIP会员哦,也是贡献者,就是写的少,CSDN是开发的居家必备哦),先下载。
四、crossDomain的思路(代理+ifream+window.name)
看他的demo,写的有点乱,半天没看懂,反正没有理解,最后想想百看不如已动,直接上手。demo大致思路是利用的window.name(这个是网上最推荐的一种,支持超过2M的name值),在需要互相通信的页面注册信息发布机制,调用信息发送对象向其他域发送信息,对应的页面会自动弹出接收到的信息(利用的可不是定时器读取,利用的是ifream的event响应)。
五、crossDomain实践
首先把封装的crossDomain.js和代理html拷贝到一个目录,注意不能放在WEB-INF下,除非配置了springMVC不拦截,设置了暴露静态资源目录,2个域都可以访问到这个2个文件。
这里我放在WebContent下,如图:
然后在父页面引入crossDomain.js,在js区域增加发送消息方法
function sendMessage(data){
RFMGR.send("a2b",data);
}
在页面加载的ready执行注册,注意看解释
//新域访问代理html页面代理地址,例如:http://10.9.212.122:8080/SPSmartCMS/thirdparty/corssDomain/proxy.html
var toProxyUrl = childProxyUrl;
//当前域访问代理html页面代理地址,例如:http://localhost:8080/SPSmartCMS/thirdparty/corssDomain/proxy.html
var localProxyUrl = mainProxyUrl;
//发送消息代理名称,跨域类型:toMain(给本地域发送消息),toSub(本地给跨域框架发送消息)
var crossType = "toSub";
//跨域对象new
var c = new crossDimain(toProxyUrl,localProxyUrl,crossType);
//这里接收子页面发来的信息
RFMGR.on("b2a",function(data){
//alert("从B页面传过来的数据:"+data);
var dataObj = eval("("+data+")");
//调用刷新左边地址的方法
reFlashLeft(dataObj);
});
父页面做的事就做完了,下面是子页面
引入crossDomain.js是必须的。
也需要注册代理
function regProxyInit(){
var curUrl = location.href;
var crossType = "toMain"; //注意跨域类型名
//2个代理地址因为不确定,所以由java后台放入model,页面取(也就是放Attribute里)
var c = new crossDimain(toProxyUrl,localProxyUrl,crossType);
RFMGR.on("a2b",function(data){
alert("从A页面传过来的数据:"+data);
});
}
也要增加发送信息方法
function sendMessage(data){
RFMGR.send("b2a",data);
}
之前说过我的子页面是不能动的,但是引入js还是可以的,上面的这个方法,包括设置栏目的方法就在一个js里,页面引入这个js文件。
这里特别说明的是,如果发送的信息有多个,需要封装成json字符串。我这里就是有多个参数信息发送,
function setLm(lmId,lmType){
var data = {};
data["lmId"] = lmId;
if(!lmType){
lmType = "channel";
}
data["lmType"] = lmType;
sendMessage(JSON.stringify(data));
}
到此跨域就解决了,点击右边的栏目,onClick里调用的setLm()方法->调用sendMessage(),父页面响应从RFMGR.on()入口,里面就接收到信息就可以写自己的逻辑了。效果就是最开始的截图了,保存再刷新右边是同域操作不用我说了吧。
现在回到先前问的一个是否同域的问题,相信大家看了下面的表就知道了。
特别注意两点:
第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,
第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
另外才补充1个前端解决跨域的思路,这个应该是比较靠谱的,没有亲测,只为开拓大家的思路
> postMessage(HTML5中的XMLHttpRequest Level 2中的API)
1) a.com/index.html中的代码:
<iframe id="ifr" src="b.com/index.html"></iframe><script type="text/javascript">window.onload
= function() {
var ifr = document.getElementById('ifr');
var targetOrigin = 'http://b.com';
// 若写成'http://b.com/c/proxy.html'效果一样
// 若写成'http://c.com'就不会执行postMessage了
ifr.contentWindow.postMessage('I was there!', targetOrigin);};</script>
2) b.com/index.html中的代码:
<script type="text/javascript"> window.addEventListener('message',
function(event){
// 通过origin属性判断消息来源地址
if (event.origin ==
'http://a.com') { alert(event.data); // 弹出"I was there!"
alert(event.source); // 对a.com、index.html中window对象的引用
// 但由于同源策略,这里event.source不可以访问window对象
} }, false);</script>