你是否遇到了localStorage不共享的情况?sessionStorage不在原来页面的clone基础上?
场景
主项目在域名main.cn中用iframe的方式嵌入其他系统,比如A系统a.domain.cn以及B系统b.domain.cn等。A,B系统通过监听postmessage方式获取主项目的用户信息存储在sessionStorage中,来让A,B系统的鉴权。但是A,B系统中通过window.open 打开的页面(同源)都因为获取不到sessionStorage而鉴权不成功,而导致不可以正常加载页面
方案汇总
一、给window.open的页面发送postMessage通信
- 修改window.open方法,给window.open的页面发送postMessage通信
typescript
export const windowOpen = (url: string, windowName = '') => {
const target = window.open(
url,
windowName,
'toolbar=no'
);
if(target){
// 不要直接postMessage,不然target接收不到,因为那个时候页面还没有加载
target.addEventListener('DOMContentLoaded',()=>{
target.postMessage({
token: '鉴权信息'
},location.origin)
})
}
};
- 监听message事件
javascript
window.addEventListener('message', (e) => {
console.log('postmessage收取到信息', e.data)
sessionStorage.setItem('token', e.data.token)
if(e.origin == location.origin){
// 重新加载页面
window.location.reload()
}
});
- 优化下,window.open是先进入login页面再跳转到目标页面,直接空白而不是进入login页面。这跟JS的event loop有关,event loop是在脚本执行后才开始轮询
javascript
const RequireAuth = ({ children }: { children: JSX.Element }) => {
const token = sessionStorage.getItem('token');
const location = useLocation();
if (!token && window.opener) {
// 解决浏览器版本升级后,iframe嵌入的形式中window.open不会携带原本的sessionStorage信息导致跳转到login页面 优化为空白页面
return ""
}
if (!token) {
return <Navigate to="login" state={{ from: location }} replace />;
}
return children;
};
export default RequireAuth;
二、改为localStorage的存储
心想sessionStorage不共享,localStorage总共享吧
修改后发现window.open的页面还是没办法正常加载,localStorage原来也不共享 了。我在思索这里面的原因是主项目跟iframe内嵌页面是跨域的。window.open打开的页面是否跨域不取决于内嵌页面,而在于主项目,所以有跨域了
最后才发现可以直接window.opener.localStorage.getItem(key)
去获取iframe内嵌页面的localStorage
javascript
if(window.opener){
return window.opener.localStorage.getItem('token');
}
PS:
localhost的访问形式。localStorage是共享的,sessionStorage是会在原来的基础上
非localhost的域名,localStorage是互不影响的,sessionStorage不复制
PS: 盲猜???浏览器对localhost的形式跨域限制比较宽容??
三、最终解决 直接获取window.opener.sessionStorage
kotlin
export const getSessionStorageValue = function (key: string) {
const val = sessionStorage.getItem(key)
if(val){
return val;
}
if(window.opener){
// fix: iframe下window.open不会携带window.opener的sessionStorage信息
return window.opener.sessionStorage.getItem(key);
}
return ""
};
总结
- sessionStorage跟localStorage都遵从同源策略
- main.cn系统中以iframe的形式嵌入B系统(b.domain.cn),在iframe中打开B系统的页面a.html,
window.open('/a')
。会被浏览器认为不同源,从而导致sessionStorage没有从iframe克隆过来,localStorage不共享 - window.open打开的如果是同源页面,会返回一个WindowProxy对象,可以访问原本的window的信息,可以获取localStorage,sessionStorage
- 通过window.opener可以获取到原本窗口的引用。
window.open
跟a
标签且rel="noopener"
的页面 - localhost模式开发跟直接a.domain.cn的开发模式,浏览器对sessionStorage跟localStorage的处理有差异性