NuxtJS 状态更改和 firebase 身份验证

Posted

技术标签:

【中文标题】NuxtJS 状态更改和 firebase 身份验证【英文标题】:NuxtJS state changes and firebase authentication 【发布时间】:2020-07-13 06:01:43 【问题描述】:

我还是新手,如有不足请见谅。

我正在使用 nuxt https://firebase.nuxtjs.org/ 的“官方”firebase 模块来访问 auth signIn 和 singOut 等 Firebase 服务。

这行得通。

但是,我在通用模式下使用 nuxt,我无法在我的页面获取函数中访问它。所以我的解决方案是将此信息保存在 vuex 存储中,并在它更改时更新它。

因此,一旦用户登录或 firebase 身份验证状态发生变化,vuex 存储中需要发生状态变化。

目前,当用户登录或 firebase 身份验证状态发生变化时,如果用户仍处于登录状态,我会将状态保存到我的商店,如下所示:

const actions = 
  async onAuthStateChangedAction(state,  authUser, claims ) 
    if (!authUser) 
      // claims = null
      // TODO: perform logout operations
     else 
      // Do something with the authUser and the claims object...
      const  uid, email  = authUser
      const token = await authUser.getIdToken()

      commit('SET_USER',  uid, email, token )
    
  

我还有一个设置状态的突变,一个获取状态和实际状态对象以及存储初始状态的getter:

const mutations = 
  SET_USER(state, user) 
    state.user = user
  


const state = () => (
  user: null
)

const getters = 
  getUser(state) 
    return state.user
  


我的问题是,在我的许多页面上,我使用 fetch 方法从 API 获取数据,然后将这些数据存储在我的 vuex 存储中。

这个 fetch 方法使用 axios 进行 api 调用,如下所示:

async fetch( store ) 
  const token = store.getters['getUser'] //This is null for a few seconds
  const tempData = await axios
       .post(
         my_api_url,
         
           my_post_body
         ,
         
           headers: 
             'Content-Type': 'application/json',
             Authorization: token
           
         
       )
    .then((res) => 
      return res.data
    )
    .catch((err) => 
    return 
    error: err
    
    console.log('error', err)
    )
    store.commit('my_model/setData', tempData)
 

Axios 需要我的 firebase 用户 ID 令牌作为发送到 API 以进行授权的标头的一部分。

当 fetch 方法运行时,状态并不总是改变或更新,因此用户的状态在状态改变之前仍然为空,这通常是大约一秒钟后,这对我来说是个问题,因为我需要来自商店的令牌来进行我的 api 调用。

在我的 fetch 方法中调用我的 axios api 之前,如何等待 store.user 状态完成更新/不为空?

我曾考虑在用户登录时使用 cookie 来存储此信息。然后,在 fetch 方法中,我可以使用 cookie 来获取令牌,而不必等待状态更改。这种方法的问题是 cookie 在更新它的令牌之前还需要等待状态更改,这意味着它将在初始页面加载时使用旧令牌。我可能仍会选择此解决方案,只是感觉这是处理此问题的错误方法。有没有更好的方法来处理这种类型的难题?

另外,当在 fetch 内部时,第一次加载将从服务器进行,​​所以我可以从 cookie 中获取令牌,但是下一次加载将来自客户端,那么我如何检索令牌然后如果存储加载时 value 仍然为 null 吗?

编辑:

我选择了 SPA 模式。经过长时间的深思熟虑,我真的不需要 nuxt 服务器,SPA 模式具有“类似服务器”的行为,您仍然可以在页面呈现之前使用 asyncdata 和 fetch 来获取数据,中间件仍然可以类似地工作并且实际上是身份验证可以在您不必使客户端和服务器与访问令牌等保持同步的情况下工作。我仍然希望在未来看到更好的解决方案,但现在 SPA 模式可以正常工作。

【问题讨论】:

您希望未经身份验证的用户访问您应用的任何部分吗? 只有登录页面,以后可能还有其他页面...... 你试过 nuxt-fire 模块吗?它使在 nuxt 应用程序中处理 firebase 身份验证变得更加容易,并且对我们非常有用。 除此之外,是的,我做到了,这就是我正在使用的,请参阅上面我提到它的描述。它工作得很好,但是,我不知道他们的服务人员是如何工作的,我确实启用了它并将 s-s-r 模式设置为 true,但我无法访问 nuxt 服务器上的 fireauth 对象,以便从登录的 firebase 用户生成 idtoken。在多次尝试使用“hacky”方式将 cookie 数据传递到服务器后,我刚刚切换了通用模式,现在使用 SPA 模式,效果很好。 【参考方案1】:

我在寻找类似问题的解决方案时遇到了这个问题。在回答这个问题之前,我有一个类似的解决方案,正如另一个答案中提到的那样,我正在寻找的是实现细节。

我使用 nuxt.js,我想到的第一个方法是制作一个 layout 组件并仅在用户通过身份验证时呈现 <Nuxt/> 指令,但使用这种方法,我只能拥有一个布局文件,如果我确实有多个布局文件,我将不得不在每个布局中实现相同的预授权机制,尽管这是可行的,因为现在我不是在每个页面中都实现它,而是在每个布局中实现它应该是少得多。

我找到了一个更好的解决方案,那就是在 nuxt 中使用 middlewares,您可以返回一个 Promise 或使用带有中间件的 async-await 来停止应用程序的挂载过程,直​​到该 Promise 得到解决。这是示例代码:

// middleware/auth.js
export default async function ( store, redirect, $axios, app ) 
  if (!store.state.auth)  // if use is not authenticated
    if (!localStorage.getItem("token")) // if token is not set then just redirect the user to login page
      return redirect(app.localePath('/login'))
    try 
      const token = localStorage.getItem("token");
      const res = await $axios.$get("/auth/validate",  // you can use your firebase auth mechanism code here
        headers: 
          'Authorization': `Bearer $token`
        
      );
      store.commit('login',  token, user: res.user ); // now just dispatch a login action to vuex store
    
    catch (err) 
      store.commit('logout'); // preauth failed, so dispatch logout action which will clear localStorage and our Store
      return redirect(app.localePath('/login'))
    
  

现在您可以在页面/布局组件中使用此中间件,如下所示:

<template>
  ...
</template>
<script>
export default 
  middleware: "auth",
  ...

</script>

【讨论】:

至于你的“布局方法”,其实用“mixins”很容易实现。没有代码重复没有本地存储检查。至少我是这样做的。【参考方案2】:

解决此问题的一种方法是在安装应用程序之前进行firebase 登录。

从 firebase 获取令牌,将其保存在 vuex 中,然后才安装应用程序。 这将确保在页面加载时您已将 Firebase 令牌保存在商店中。

为您不想在没有登录的情况下访问的页面添加路由检查,以在商店中查找令牌(firebase 一个或另一个),如果不存在则重定向到另一个路由。

【讨论】:

谢谢,但是如果用户已经登录然后关闭浏览器的情况呢?然后将应用程序打开到同一页面。用户的 vuex 状态最初将为 null,直到 firebase auth 状态发生变化,这给我留下了同样的问题。 您可以使用 localStorage 来保存 firebase 令牌,并在安装应用程序之前从 localStorage 加载它并将其保存在 vuex 中。如果您使用另一个令牌进行身份验证,请保存该令牌。重要的是,当您挂载应用程序时,您可以确定用户是经过身份验证的还是匿名的。 我最大的问题是 firebase id 令牌仅在 1 小时内有效。即使我将它存储在本地存储中,它们仍然需要更新。因此,当用户在 2 小时后返回并再次打开他的浏览器时需要刷新它,但是,如果它使用本地存储的令牌,它将无法工作...... 这实际上并不重要。检查令牌是否有效,如果它没有被删除,那么您将继续进行非身份验证用户的流程。 谢谢,我现在正在尝试。抱歉,如果是愚蠢的问题,但我如何在安装应用程序之前强制登录?我有一个登录组件,用户在其中输入电子邮件和密码,然后用户被定向到主屏幕......

以上是关于NuxtJS 状态更改和 firebase 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何在 vue 上处理 Firebase 身份验证

用户在 Vue.js 和 Firebase 中注册后更改导航栏的状态

NuxtJS / Vuex | nuxtServerInit 和 fetchData 操作未填充用户状态

在 Flutter 中使用 StreamProvider 4.0.1 从 Firebase 监控身份验证状态

Firebase 停止监听 onAuthStateChanged

如何在 Vue 中设置身份验证状态更改?