OAuth 弹窗跨域安全 React.js
Posted
技术标签:
【中文标题】OAuth 弹窗跨域安全 React.js【英文标题】:OAuth popup cross-domain security React.js 【发布时间】:2020-03-03 01:26:57 【问题描述】:我对如何使用弹出窗口 (window.open
) 在 React 中实现 OAuth 感兴趣。
例如我有:
mysite.com
— 这是我打开弹出窗口的地方。
passport.mysite.com/oauth/authorize
— 弹出窗口。
主要问题是如何在window.open
(弹出窗口)和window.opener
之间建立连接(众所周知,window.opener 由于跨域安全性为空,因此我们不能再使用它了)。
⇑
window.opener
每当您导航到不同的主机时都会被删除(出于安全原因),没有办法绕过它。如果可能的话,唯一的选择应该是在一个框架内付款。顶部文档需要保留在同一主机上。
方案:
可能的解决方案:
-
使用
setInterval
描述的here 检查打开的窗口。
那么 2019 年最好的推荐方法是什么?
React 包装器 - https://github.com/Ramshackle-Jamathon/react-oauth-popup
【问题讨论】:
2019 年,localStorage 支持要好得多。我会使用 localStorage 方法(在***.com/questions/18625733/… 中描述),因为它似乎不是一个解决方法。父窗口不需要定期检查子窗口状态。setInterval
可以作为 localStorage 的后备
@KhanhTO,是的,我完全同意你关于 localStorage
的看法,但它只适用于同一个域,因此不适用于我的情况
完成 OAuth 后,子窗口将重定向回您的域,您现在与父窗口在同一个域中
@KhanhTO,嗯,这是个好主意!我应该知道..
如果浏览器在重定向回我们的域后恢复window.opener
会更好,但事实并非如此
【参考方案1】:
由Khanh TO 推荐。带有 localStorage 的 OAuth 弹出窗口。基于react-oauth-popup。
方案:
代码:
oauth-popup.tsx:
import React, PureComponent, ReactChild from 'react'
type Props =
width: number,
height: number,
url: string,
title: string,
onClose: () => any,
onCode: (params: any) => any,
children?: ReactChild,
export default class OauthPopup extends PureComponent<Props>
static defaultProps =
onClose: () => ,
width: 500,
height: 500,
url: "",
title: ""
;
externalWindow: any;
codeCheck: any;
componentWillUnmount()
if (this.externalWindow)
this.externalWindow.close();
createPopup = () =>
const url, title, width, height, onCode = this.props;
const left = window.screenX + (window.outerWidth - width) / 2;
const top = window.screenY + (window.outerHeight - height) / 2.5;
const windowFeatures = `toolbar=0,scrollbars=1,status=1,resizable=0,location=1,menuBar=0,width=$width,height=$height,top=$top,left=$left`;
this.externalWindow = window.open(
url,
title,
windowFeatures
);
const storageListener = () =>
try
if (localStorage.getItem('code'))
onCode(localStorage.getItem('code'));
this.externalWindow.close();
window.removeEventListener('storage', storageListener);
catch (e)
window.removeEventListener('storage', storageListener);
window.addEventListener('storage', storageListener);
this.externalWindow.addEventListener('beforeunload', () =>
this.props.onClose()
, false);
;
render()
return (
<div onClick=this.createPopup)>
this.props.children
</div>
);
app.tsx
import React, FC from 'react'
const onCode = async (): Promise<undefined> =>
try
const res = await <your_fetch>
catch (e)
console.error(e);
finally
window.localStorage.removeItem('code'); //remove code from localStorage
const App: FC = () => (
<OAuthPopup
url=<your_url>
onCode=onCode
onClose=() => console.log('closed')
title="<your_title>">
<button type="button">Enter</button>
</OAuthPopup>
);
export default App;
【讨论】:
【参考方案2】:我曾经在使用 window.open/window.opener bug on ms-edge 的 oauth 登录流程中遇到问题
这个问题之前我的流程是
在登录按钮上单击打开一个弹出窗口 成功登录后,oauth 应用重定向到我的域页面 然后我在弹出窗口 (window.opener.fn) 中使用来自 oauth 响应和父窗口的数据调用父窗口的函数,然后关闭子弹出窗口这个问题之后我的流程是
在登录按钮上单击打开一个弹出窗口 创建一个 setinterval 以防万一(window.opener 未定义) 成功登录后,oauth 应用重定向到我的域页面 检查 window.opener 是否可用,然后从上述流程中执行 #3 并清除Interval 如果 window.opener 不可用,那么由于我在我的域页面上,我尝试设置 localstorage 并尝试从父窗口的 setInterval 函数内部读取 localstorage,然后清除 localstorage 和 setInterval 并继续。 (为了向后兼容)如果 localstorage 也不可用,则设置一个客户端 cookie,其数据的过期时间很短(5-10 秒),并尝试在 setInterval 函数中读取 cookie(document.cookie)父窗口并继续。【讨论】:
以上是关于OAuth 弹窗跨域安全 React.js的主要内容,如果未能解决你的问题,请参考以下文章
Office 365 OAuth2登录认证如何实现跨域请求?