Vuejs路由导航保护异步身份验证状态请求

Posted

技术标签:

【中文标题】Vuejs路由导航保护异步身份验证状态请求【英文标题】:Vuejs route navigation guard on async auth state request 【发布时间】:2018-09-12 07:07:32 【问题描述】:

我正在使用 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。

这应该在存储中而不是在根实例上创建的钩子吗?任何帮助是极大的赞赏。这种模式让我很难过。

【问题讨论】:

我会将身份验证逻辑放在类似模块的服务中,从该逻辑中删除所有路由,然后只在路由器中处理路由,您已经在其中进行了大部分操作(在导航守卫中)。 @frank 我觉得firebase标签与这个问题无关。 我只是用更具体的 [firebase-authentication] 替换了通用的 [firebase],这就是 OP 在这里使用的。随意进行任何您认为可以提高得到回答的机会的编辑。 @EmileBergeron 但路由和身份验证需要相互了解。如果身份验证状态变为“未授权”,则路由需要返回登录。 【参考方案1】:

快速修复

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

   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');。请参阅下文以获得更深入的答案。

深入

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

我喜欢将 Vuex 存储实例放在一个独立的模块中,这样我就可以在其他地方使用它,而不仅限于 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)

其实这个导航守卫可以注册在上面提到的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

【讨论】:

getUser返回null怎么办?当 auth 模块尚未验证用户,但调用了 beforeEach 回调时,就会发生这种情况。如何一直等待用户认证并显示加载页面?

以上是关于Vuejs路由导航保护异步身份验证状态请求的主要内容,如果未能解决你的问题,请参考以下文章

在反应中使用 cookie 的客户端身份验证和受保护的路由

使用身份验证保护 React Route 中内容的正确方法?

找到路由器(用于 Relay Modern)受保护的身份验证路由

使用 redux 保护的路由和身份验证反应路由器 v4

Node/Express 的 Passport 身份验证中间件 - 如何保护所有路由

Laravel 多重身份验证保护路由多个中间件不起作用