Vuejs 路由导航守卫异步身份验证状态请求

Vuejs route navigation guard on async auth state request

我正在使用 firebase 在我的 vuejs 应用程序中进行身份验证。在我的根 (main.js) vue 组件中。

  created() {
    auth.onAuthStateChanged((user) => {
      this.$store.commit('user/SET_USER', user);
      if (user && user.emailVerified) {
        this.$store.dispatch('user/UPDATE_VERIFIED', {
          userId: user.uid,
        });
        // SUCCESSFUL LOGIN
        this.$router.replace('dashboard');
      } else if (user && !user.emailVerified) {
        this.$router.replace('verify');
      }
    });

所以基本上,我的路由器导航守卫正在检查身份验证状态并在每条路线之前进行路由。

router.beforeEach((to, from, next) => {
  const currentUser = firebaseApp.auth().currentUser;
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  const allowUnverified = to.matched.some(record => record.meta.allowUnverified);

  if (requiresAuth && !currentUser) {
    next('login');
  } else if (currentUser
              && !currentUser.emailVerified
              && !allowUnverified
              && to.path !== '/verify') {
    next('verify');
  } else if (!requiresAuth && currentUser) {
    next('dashboard');
  } else {
    next();
  }
});

发生的事情是,当您刷新页面(使用有效的身份验证令牌)时,您点击了路由守卫的分支 1,stateChanges 并调用处理程序以重定向到 /dashboard。登录时刷新,总是将您带到仪表板路线,而不是您所在的当前路线。

我该如何处理这种情况?为每个身份验证组件添加一个 beforeEnter 守卫对我来说很难闻 Data Fetching Vue Docs.

这应该在商店中而不是在根实例上创建的挂钩吗?任何帮助是极大的赞赏。这个模式难倒我了。

快速修复

如果用户存在且不需要身份验证,为什么将他重定向到另一个页面(仪表板)?

  } else if (!requiresAuth && currentUser) {
    next('dashboard');
  } else {
    next();
  }

您可以让路由继续。

  if (requiresAuth && !currentUser) {
    next('login');
  } else if (requiresAuth && !currentUser.emailVerified && !allowUnverified) {
    next('verify');
  } else {
    next();
  }

您还需要从 onAuthStateChanged 回调中删除 this.$router.replace('dashboard');。请参阅下文以获得更深入的答案。

深入

我会避免在 Vue 组件中放置 authenticationrouting 逻辑。虽然有一天它可能有意义,但在这种情况下,它是完全可以避免的。

我喜欢将 Vuex store 实例放在一个独立的模块中,这样我就可以在别处使用它,而不仅限于 Vue。

import Vue from 'vue';
import Vuex from 'vuex';
// Configuration is generated with a function, so testing the store is easier
import getConfig from './config';

Vue.use(Vuex);

export default new Vuex.Store(getConfig());

因此可以将身份验证逻辑移至 auth.js 服务。

import store from './store';
import router from './router';

// other auth related code, you can even export a common API
// to use within your app.
// like this simple wrapper, which hides the fact that you're using firebase
export function getUser() {
  return firebaseApp.auth().currentUser;
}
export default { getUser }

auth.onAuthStateChanged((user) => {
  // store/business logic should be inside the store.
  store.dispatch('user/AUTH_STATE_CHANGED', user);
  // minimal handling of mandatory steps in the login or sign up process
  if (!user) {
     router.push('login');
  } else if (user && !user.emailVerified) {
    router.push('verify');
  }
});

如果用户正确登录,则无需重定向到此处。由于全局导航守卫将完成大部分重定向工作,您可以 redirect to the current route with router.go() or router.push(router.currentRoute).

其实这个navigation guard可以在上面提到的auth.js服务里面注册。

router.beforeEach((to, from, next) => {
  const currentUser = getUser();
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  const allowUnverified = to.matched.some(record => record.meta.allowUnverified);

  if (requiresAuth && !currentUser) {
    next('login');
  } else if (requiresAuth && !currentUser.emailVerified && !allowUnverified) {
    next('verify');
  } else {
    next();
  }
});

然后,为登录后无法访问的路线添加简单的导航守卫。

path: '/login',
beforeEnter: (to, from, next) => {
    if (auth.getUser()) {
        next('dashboard');
    } else {
        next();
    }
},

要点是:仅在导航到受限页面时强制重定向。

当登录用户刷新页面时,没有理由重定向到 /dashboard。如果同一用户尝试导航到登录,则有理由重定向到 /dashboard.