我的web应用程序在前端使用React,在后端使用Flask。
我想添加AWS Cognito用于用户管理。根据文档,这只需要在前端包装一个export
语句,即更改一行
export default App;
在App.js
文件中(定义主应用程序React组件)到
export default withAuthenticator(App, true);
加上添加所需的CCD_ 3语句。
这个微不足道的变化让我获得了生产中使用的任何合理的web应用程序所需的所有以用户为中心的功能,例如
- 用户注册
- 电子邮件验证
- 登录/注销
- 忘记密码
- 等等。pp
包括
- 主题UI
- 电子邮件发送服务
- 联合登录(例如Google、Facebook等)
问题:我不知道如何将其与后端集成。
方法:(我认为应该是这样)
- 现有用户使用Cognito提供并由前端显示的UI元素登录到web应用程序
- 成功登录后,Cognito通过
currentAuthenticatedUser()
提供用户数据;其中包括JSON Web令牌(JWT) - 每当前端向后端发送请求时,它都会将JWT作为请求头进行传递
- 后端使用该JWT来确定用户是否正确登录,向哪个用户发出请求,以及该用户是否有足够的权限
我已经实现了常见的Flask/login
或/logout
请求处理程序和login_required
装饰器,并且还可以传递JWT和解码声明但是,我不知道前端应该在哪里通知后端用户登录。
我试着用Cognito Post Authentication Lambda触发器来做这件事,但这有很多巨大的缺点:
对于初学者来说,由用户登录触发的AWS lambda函数必须能够访问后端,因此它必须是公开的。出于安全考虑,我实际上更希望后端只对前端可见/可访问。此外,在开发过程中,我在MacBook的Docker容器中同时运行前端和后端;这种方法迫使我至少在云中运行后端。
然而,最大的问题是它不起作用:从后端的角度来看,来自前端和Cognito的请求发生在不同的会话中,因此即使用户使用Cognito(通过前端)登录,并且Cognito通知后端该登录(通过lambda触发器),从后端的角度来看,从前端到后端的任何后续请求都与后端和Cognito之间的(已验证的)会话无关,因此未经验证。
好吧,除了巨大的缺点和它不起作用的事实之外,lambda触发器方法也让人觉得完全过头了。
更新:我不知道为什么我之前没有看到这一点,但Amplify文档中有一节确切地解释了如何自定义withAuthenticator
。但是,我不想像示例中那样覆盖render()
,而是覆盖signIn()
(或者我相信是这样)。我已经成功地实现了向http://mybackend.mydomain.com/login
发送请求的代码,该请求带有适当的头以登录后端,但我似乎不知道在哪里/如何调用该代码。
我已经从上面链接的Amplify文档中复制了代码,在node_modules/aws-amplify-react/dist/Auth/SignIn.js
的本地源代码文件夹中查看了SignIn
JSX源代码和("rendered"/"builded"?)版本,并尝试了这个:
class MySignIn extends SignIn {
async signIn() {
console.log('MySignIn.signIn() start');
super.signIn()
.then(data => console.log(data))
.catch(err => console.log(err));
console.log('MySignIn.signIn() end');
}
}
但这一切让我看到的只是一个空页面,日志中没有任何消息。
新问题:我如何实现MySignIn
,以便它完成所有SignIn
的工作,但在成功登录后,我还需要做额外的工作?
非常感谢您的考虑!:-)
我检查了代码,signIn
函数似乎很难扩展,因为它不返回任何内容,所以使用super
和then
没有意义,因为解析函数不会接收任何数据。这就是为什么你的日志是空的。为了利用它,你需要从根本上重新定义它。
您可以做的是重新实现changeState
方法本身,因为在这种情况下,您可以只调用父方法,然后执行任何您想要的操作。
但是,仅扩展任何Auth
组件都存在问题,因为如果您想对其进行子类化,则必须重新实现render
或showComponent
函数,因为父组件(因为父组件现在已隐藏)不会返回任何内容。(这可能是他们应该在文档中澄清的内容,尽管在他们的示例中render
函数被明确地重新定义)。
调用父类的showComponent
方法而不返回空白页的一个简单方法是从每个组件接收的隐藏组件列表中删除父类。
因此,加上changeState
方法的扩展,看起来像这样:
class MySignIn extends SignIn {
showComponent(theme) {
const signIn = this.props.hide.indexOf(SignIn)
this.props.hide.splice(signIn, 1) // make sure SignIn is not hidden
return super.showComponent(theme) // call showComponent from SignIn
}
changeState(state, data) {
super.changeState(state, data)
if (state !== 'signedIn') return
console.log(data)
// make the request to the backend
}
}
现在,您可以像文档中显示的那样传递MySignIn
。
请记住,更改props
应该是一种反模式,但我还没有找到另一种不涉及完全重新定义showComponent
(或者更糟的是,render
)方法的选项,因为在父对象中,这就是它所寻找的。
PR的一个想法可能是使这些方法(如import
0)更容易扩展。
老答案:
这无法实现,因为onStateChange
属性在组件实际实例化时被覆盖
另一种选择是将函数传递给onStateChange
,作为signin组件的道具,并检查signedIn
状态,如下所示:
function onStateChange(state, data) {
if (state !== 'signedIn') return
console.log(data)
// do the request to the backend here
}
在您的withAuthenticator
定制中
export default withAuthenticator(App, false, [
<SignIn onStateChange={ onStateChange } />,
// the rest