Cognito / Flask / React:如何登录后端?

ssc*_*ssc 5 authentication flask jwt reactjs amazon-cognito

我的 Web 应用程序在前端使用React ,在后端使用Flask 。

我想添加AWS Cognito进行用户管理。根据文档,这只需要export在前端包装一条语句,即更改一行

export default App;
Run Code Online (Sandbox Code Playgroud)

在文件中App.js(定义主应用程序 React 组件)

export default withAuthenticator(App, true);
Run Code Online (Sandbox Code Playgroud)

添加所需的import语句。

这个微不足道的改变让我获得了生产中使用的任何合理的 Web 应用程序所需的所有以用户为中心的功能,例如

  • 用户注册
  • 电子邮件验证
  • 登录/注销
  • 忘记密码
  • 等页。

包括

  • 主题化用户界面
  • 电子邮件发送服务
  • 联合登录(例如 Google、Facebook 等)

问题:我不知道如何将其与后端集成。

方法:(我认为应该如此)

  1. 现有用户使用 Cognito 提供并由前端显示的 UI 元素登录到 Web 应用程序
  2. 成功登录后,Cognito 通过以下方式提供用户数据currentAuthenticatedUser():其中包括JSON Web 令牌(JWT)
  3. 每当前端向后端发送请求时,它都会将该 JWT 作为请求标头传递
  4. 后端使用该 JWT 来确定用户是否已正确登录、请求是针对哪个用户以及该用户是否具有足够的权限

我已经实现了通常的 Flask/login/logout请求处理程序和login_required装饰器,并且传递 JWT 和解码声明工作正常;但是,我不知道前端应该在哪里通知后端有关用户登录的信息。

我尝试使用Cognito Post Authentication Lambda Trigger来执行此操作,但这有许多巨大的缺点:

对于初学者来说,由用户登录触发的AWS lambda函数必须能够访问后端,因此它必须是公开可用的。出于安全原因,我实际上更希望后端仅对前端可见/可访问。此外,在开发过程中,我在 MacBook 上的 Docker 容器中运行前端和后端;这种方法迫使我至少在云中运行后端。

但最大的问题是它不起作用:从后端的角度来看,来自前端和 Cognito 的请求发生在单独的会话中,因此即使用户使用 Cognito 登录(通过前端)并且 Cognito 也会通知后端有关此登录的信息(通过 lambda 触发器),从前端到后端的任何后续请求(从后端角度来看)都与后端和 Cognito 之间的(经过身份验证的)会话无关,因此未经身份验证。

好吧,除了巨大的缺点和不起作用的事实之外,lambda 触发器方法还让人感觉完全是矫枉过正。

更新:我不知道为什么我之前没有看到这一点,但 Amplify 文档中有一个部分准确解释了如何自定义withAuthenticator. 但是,我不想像render()示例中那样覆盖,但是signIn()(或者我相信)。我已经成功实现了代码,http://mybackend.mydomain.com/login以使用适当的标头发送请求以登录后端,但我似乎无法弄清楚在哪里/如何调用该代码。

我已经从上面链接的 Amplify 文档中复制了代码,查看了SignIn JSX 源代码和本地源代码文件夹中的(“渲染”/“构建”?)版本node_modules/aws-amplify-react/dist/Auth/SignIn.js,并尝试了以下操作:

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');
  }
}
Run Code Online (Sandbox Code Playgroud)

但这给我带来的只是一个空白页面,日志中没有任何消息。

新问题:我如何实现,MySignIn以便它完成所有SignIn工作,但成功登录后,还需要我完成额外的工作吗?

非常感谢您的考虑!:-)

Lui*_*duz 2

我检查了代码,该signIn函数似乎很难扩展,因为它不返回任何内容,因此使用没有意义superthen而且解析函数不会接收任何数据。这就是为什么你的日志是空的。要使用它,您需要从头开始重新定义它。

您可以做的是重新实现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
  }
}
Run Code Online (Sandbox Code Playgroud)

MySignIn现在您可以像文档中显示的那样通过。

请记住,更改props应该是一种反模式,但我还没有找到另一个不涉及完全重新定义showComponent(或更糟糕的是render)方法的选项,因为在父级中这就是它所寻找的。

PR 的一个想法可能是使这些方法(如signIn)更容易扩展。

旧答案:

这是无法实现的,因为该onStateChange属性在组件实际实例化时被覆盖。

另一种选择是将函数作为onStateChangeprop 传递给您的登录组件并检查状态signedIn,如下所示:

function onStateChange(state, data) {
  if (state !== 'signedIn') return
  console.log(data)
  // do the request to the backend here
}
Run Code Online (Sandbox Code Playgroud)

并且在您的定制中withAuthenticator

export default withAuthenticator(App, false, [
  <SignIn onStateChange={ onStateChange } />,
  // the rest
Run Code Online (Sandbox Code Playgroud)