【问题标题】:Checking auth token valid before route enter in Vue router在路由进入Vue路由器之前检查身份验证令牌是否有效
【发布时间】:2018-02-25 23:24:47
【问题描述】:

我有一个简单的用例,我的应用程序使用vue-routervuex。然后store 包含一个user 对象,它的开头是null。从服务器验证用户后,它会发回一个user 对象,其中包含一个JWT 身份验证令牌,该令牌分配给存储中的user 对象。现在让我们假设用户在 3 小时后回来并尝试访问路线或执行任何其他操作,考虑到那时 auth 令牌已过期,检查该情况的最佳方法是什么(需要调用 axios post 到检查它)并将用户重定向到login页面。我的应用程序将包含大量组件,因此我知道我可以编写逻辑来检查每个组件的 mounted 挂钩中的令牌是否有效,但这意味着重复所有组件。此外,我不想使用beforeEach 导航守卫,因为我无法向用户显示任何视觉反馈,例如checking...loading...

【问题讨论】:

    标签: vue.js vuejs2 vue-component vue-router vuex


    【解决方案1】:

    我在我的一个项目中做了类似的事情,实际上很难处理这些类型的情况,但是您可以在受保护的路由中添加beforeEnter 保护,然后在身份验证失败时重定向。

    const guard = function(to, from, next) {
      // check for valid auth token
      axios.get('/api/checkAuthToken').then(response => {
          // Token is valid, so continue
          next();
      }).catch(error => {
          // There was an error so redirect
          window.location.href = "/login";
      })
    };
    

    那么在你的路线上你可以这样做:

    {
      path: '/dashboard',
      component: Dashboard,
      beforeEnter: (to, from, next) => {
        guard(to, from, next);
      }
    },
    

    您可能会注意到我使用了location.href 而不是router.push。我这样做是因为我的登录表单受 csrf 保护,所以我需要一个新的 csrf_token。

    如果用户尝试在不更改路由的情况下与您的页面交互(即他们单击按钮并获得 401 响应),您的另一个问题将是。为此,我发现检查每个 axios 请求的身份验证并在收到 401 响应时重定向到 login 是最简单的方法。

    关于在保护检查期间添加加载微调器,您可以简单地将加载标志添加到您的 vuex 商店,然后将您的商店导入您的路由器。老实说,虽然我不会打扰,但在一个不错的生产服务器上,检查会很快完成,以至于用户不太可能看到它。

    【讨论】:

    • 我不会在 vuerouter 中使用`window.location.href`
    • @deathangel908 这专门用于刷新 Laravel CSRF 令牌,该令牌将在一段时间不活动后过期,因此此时路由器推送将意味着用户需要手动刷新该页面才能登录,我发现从本质上强制会话刷新登录页面更容易。这只是 Laravel 的一个怪癖,必须解决,否则我不会这样做。
    • guard 函数使页面永远重定向到自身。
    【解决方案2】:

    试试 Vue.JS Mixins

    您可以定义一个全局 Mixin 并通过 Vue.use(myMixin) 使用它 - 然后所有组件都将继承此 mixin。如果你在 mixin 上定义了一个 mounted 或者更好的 activated 钩子,它将在每个组件上被调用。

    在那里,您可以使用组件可以做的所有事情 - this 将指向您的组件。如果组件本身也定义了一个钩子,那么相同类型的 mixin 钩子将在 组件自己的钩子之前运行。

    或者尝试单个顶级登录组件

    我们使用了一些不同的解决方案——我们有一个单独的组件来处理所有与登录相关的事情,它存在于父 index.html 中的路由器视图之外。该组件始终处于活动状态,可以隐藏 div 路由器视图并覆盖加载消息或登录屏幕。对于 Intranet 应用程序,只要浏览器保持打开状态,此组件还将使用轮询来保持会话活动。

    您可以将您的路由器导航加载到此组件。 - 因此,想要触发路由器导航的子组件只需设置一个全局反应属性navigateTo,由顶级身份验证组件监视。这将触发身份验证检查,可能是登录工作流,然后顶级组件将调用 $router.push() 使用这种方法,您可以完全控制任何导航。

    【讨论】:

    • 假设如果我在mixin中有一个mounted钩子,我如何确保组件级别mounted在某些操作完成之前没有被调用?
    • 我认为没有一种干净的方法可以做到这一点。更好的方法是为此操作定义一个新的自定义选项名称。例如ready - 并定义您的 Mixin-Method 以在您的操作完成后调用组件的 ready 方法。
    • jsfiddle.net/pam7u01L 一个使用 mixin 注册自定义“就绪”函数的示例,该函数在完成一些 ajax 调用或类似操作后调用...
    【解决方案3】:

    当某些请求发生时,您可以使用interceptors 静默获取身份验证令牌。

        axios.interceptors.response.use(function (response) {
             return response;
           }, function (error) {
    
                  const originalRequest = error.config;
    
                 if (error.response.status === 401 && !originalRequest._retry) {
    
                  originalRequest._retry = true;
    
                  const rToken = window.localStorage.getItem('rToken');
                  return axios.post('url/to/get/refresh/token', { rToken })
                         .then(({data}) => {
                         window.localStorage.setItem('token', data.token);
                         window.localStorage.setItem('rToken', data.refreshToken);
                         axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
                        originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
                        return axios(originalRequest);
               });
          }
    
          return Promise.reject(error);
       });
    

    【讨论】:

    • 您是否将其视为路由器中的异步中间件?
    • 如果你的组件中没有请求怎么办?尽管您的令牌已过期,您仍将被允许导航
    【解决方案4】:

    因为您使用vuex,所以您可以添加一些状态,例如isLoadingisChecking

    并且在您的router.beforeEach 中,您可以检查并设置isLoadingisChecking 遵循您当前的检查状态。然后你可以在这个状态下显示加载消息。

    【讨论】:

      【解决方案5】:

      在我们的 route.js 中,我们签入 beforeEnter 钩子,用户拥有令牌或 不是。

      route.js

      {
         path: '/dashboard',
         name: dashboard,
         meta: {
           layout: 'home-layout'
         },
         components: {
           default: Dashboard,
           header: UserHeader
         },
         beforeEnter: ifAuthenticated,
       }
      

      route.js

      const ifAuthenticated = (to, from, next) => {
       if (localStorage.getItem(token)) {
         next();
         return;
       }
       router.push({ 
         name: 'login',
         params: {
           returnTo: to.path,
           query: to.query,
         },
       });
      };
      

      【讨论】:

      • 如果令牌在服务器端被强制过期,您将被允许导航,因为您的 localStorage 仍然包含令牌密钥
      猜你喜欢
      • 2018-02-04
      • 2017-09-24
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多