【问题标题】:Firebase Auth and Vue-routerFirebase 身份验证和 Vue 路由器
【发布时间】:2017-09-24 16:27:12
【问题描述】:

我正在尝试使用 firebase 对 Vue.js 应用程序进行身份验证。

我有一个问题,如果在登录时尝试直接访问受登录保护的 URL,路由器将在 firebase.js 有时间返回身份验证响应之前加载并检查身份验证状态。这会导致用户被退回到登录页面(当他们已经登录时)。

如何延迟 vue-router 导航,直到从 firebase 检索到身份验证状态?我可以看到firebase将身份验证数据存储在localStorage中,检查它是否存在作为初步身份验证检查是否安全?理想情况下,最终结果是在用户通过身份验证时显示加载微调器或其他内容,然后他们应该能够访问他们导航到的页面。

路由器/index.js

let router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    },
    {
      path: '/example',
      name: 'Example',
      component: Example,
      beforeEnter: loginRequired
    }
})

function loginRequired (to, from, next) {
  if (authService.authenticated()) {
    next()
  } else {
    next('/login')
  }
}

auth.js

import * as firebase from 'firebase'

var config = {
    // firebase config
}

firebase.initializeApp(config)

var authService = {

  firebase: firebase,
  user: null,

  authenticated () {
    if (this.user == null) {
      return false
    } else {
      return !this.user.isAnonymous
    }
  },

  setUser (user) {
    this.user = user
  },

  login (email, password) {
    return this.firebase.auth().signInWithEmailAndPassword(email, password)
      .then(user => {
        this.setUser(user)
      })
  },

  logout () {
    this.firebase.auth().signOut().then(() => {
      console.log('logout done')
    })
  }
}

firebase.auth().onAuthStateChanged(user => {
  authService.setUser(user)
})

export default authService

app.vue

<template>
  <div id="app">
    <p v-if="auth.user !== null">Logged in with {{ auth.user.email }}</p>
    <p v-else>not logged in</p>
    <router-view v-if="auth.user !== null"></router-view>
  </div>
</template>

<script>
import authService from './auth'

export default {
  name: 'app',
  data () {
    return {
      auth: authService
    }
  }
}
</script>

【问题讨论】:

    标签: javascript firebase vue.js firebase-authentication vue-router


    【解决方案1】:

    要延迟身份验证状态,您需要做的就是

    • a) 在挂载应用之前,使用 firebase auth onAuthStateChanged 方法。
    • b) 在您的路由器文件中,将元数据添加到您要添加登录保护的父路由
    • c) 使用 router.beforeEach 进行身份验证以及您希望如何将用户重定向到。我将为以下每种情况提供示例: a) 阶段
        firebase.auth().onAuthStateChanged(function(user) {
            console.log(user)
            new Vue({
                router,
                render: h => h(App),
              }).$mount('#app')
            }
        });
    

    阶段b)

        ....
        ....
        ....
        {
            path: "/dashboard",
            name: "dashboard",
            component: Dashboard,
            meta: { requiresAuth: true },//Add this
            children: [
                {
                    path: "products",
                    name: "products",
                    component: Products,
                },
            ],
        ....
        ....
        ....
    

    阶段c)

        router.beforeEach((to, from, next) => {
            const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
            const currentUser = firebase.auth().currentUser
            if(requiresAuth && !currentUser) {
                next("/")
            } else if(requiresAuth && currentUser) {
                next()
            }else{
                next()
            }
        })
    

    我相信你可以走这条路。

    【讨论】:

      【解决方案2】:

      你有两个选择:

      1) 使用组件中的 beforeRouteEnter

      export default {
              name: "example",
              ....
              beforeRouteEnter(to, from, next){
                  if (authService.authenticated()) {
                      next()
                  } else {
                      next('/login')
                  }
              },
      }
      

      2) 从路由器使用 beforeResolve

      router.beforeResolve((to, from, next) => {
      
          if(to.fullPath === '/example' && !authService.authenticated()){
              next('/login')
          }else{
              next()
          }
      })
      

      Life-circle of Vue route guards

      【讨论】:

        【解决方案3】:

        Richard's 为基础,适用于使用常规 Vue(不是 Vuex)的人

        main.js

        //initialize firebase
        firebase.initializeApp(config);
        let app: any;
        firebase.auth().onAuthStateChanged(async user => {
        if (!app) {
            //wait to get user
            var user = await firebase.auth().currentUser;
        
            //start app
            app = new Vue({
              router,
              created() {
                //redirect if user not logged in
                if (!user) {
                  this.$router.push("/login");
                }
              },
              render: h => h(App)
            }).$mount("#app");
          }
        });
        

        router.js

        //route definitions
        //...
        router.beforeEach((to, from, next) => {
          const currentUser = firebase.auth().currentUser;
          const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
        
          if (requiresAuth && !currentUser) {
            const loginpath = window.location.pathname;   
            next({ name: 'login', query: { from: loginpath } });
          } else if (!requiresAuth && currentUser) {
            next("defaultView");
          } else {
            next();
          }
        });
        

        【讨论】:

        • 感谢这个例子。我进一步简化了它——我认为不需要等待firebase.auth().currentUser 的结果。一旦onAuthStateChanged 被触发,我发现firebase.auth().currentUser 已准备就绪。
        【解决方案4】:

        我刚刚遇到了同样的问题,最终将 Vue 对象的创建延迟到了第一个 onAuthStatedChanged。

        # main.js
        // wait for first firebase auth change before setting up vue
        import { AUTH_SUCCESS, AUTH_LOGOUT } from "@/store/actions/auth";
        import { utils } from "@/store/modules/auth";
        let app;
        firebase.auth().onAuthStateChanged(async user => {
          if (!app) {
            if (user) {
              await store.dispatch(AUTH_SUCCESS, utils.mapUser(user));
            } else {
              await store.dispatch(AUTH_LOGOUT);
            }
            app = new Vue({
              router,
              store,
              i18n,
              render: h => h(App)
            }).$mount("#app");
          }
        });
        

        然后在我的路线中,我照常检查,如果他们最终进入登录路线,我只需将它们推送到我的概览页面,这是我的仪表板页面。

        #router.js
        router.beforeEach((to, from, next) => {
          let authenticated = store.getters.isAuthenticated;
          if (to.matched.some(record => record.meta.requiresAuth)) {
            // this route requires auth, check if logged in
            // if not, redirect to login page.
            if (!authenticated) {
              next({
                name: "Login",
                query: { redirect: to.fullPath }
              });
            } else {
              next();
            }
          } else {
            // doesn't require auth, but if authenticated already and hitting login then go to overview
            if (authenticated && to.name === "Login") {
              next({
                name: "Overview"
              });
            }
            next(); // make sure to always call next()!
          }
        });
        

        【讨论】:

        • requiresAuth 是添加到任何需要身份验证的路由的元字段。它不必如此命名,但我就是这样命名的。您可以在其中添加要检查的任何其他字段。您可以添加一个名为 requiresAdmin 的字段,然后在检查之前检查该字段以查看他们是否是管理员。 router.vuejs.org/guide/advanced/meta.html
        【解决方案5】:

        Firebase 总是在启动时触发身份验证状态更改事件,但不是立即触发。

        您需要让 authService.authenticated 返回一个承诺,以便等待 Firebase 完成其用户/身份验证初始化。

        const initializeAuth = new Promise(resolve => {
          // this adds a hook for the initial auth-change event
          firebase.auth().onAuthStateChanged(user => {
            authService.setUser(user)
            resolve(user)
          })
        })
        
        const authService = {
        
          user: null,
        
          authenticated () {
            return initializeAuth.then(user => {
              return user && !user.isAnonymous
            })
          },
        
          setUser (user) {
            this.user = user
          },
        
          login (email, password) {
            return firebase.auth().signInWithEmailAndPassword(email, password)
          },
        
          logout () {
            firebase.auth().signOut().then(() => {
              console.log('logout done')
            })
          }
        }
        

        您无需从signInWith... 承诺中调用setUser,因为这已经由initializeAuth 承诺处理。

        【讨论】:

        • actually authenticated() 仍然返回 Promise 对象而不是布尔值 :(
        • @ilumin 我不太确定你的意思。这是为了回报一个承诺。我特别说“你需要让authService.authenticated返回一个promise
        猜你喜欢
        • 2018-02-04
        • 2018-04-13
        • 2015-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-11
        • 2017-05-05
        • 2021-11-07
        相关资源
        最近更新 更多