基于 Laravel + Vue 构建 API 驱动的前后端分离应用系列(二十八) —— 通过 Vuex + Vue Router 导航守卫在前端实现认证路由保护

我们已经在上一篇教程中实现了通过 Vue 组件实现用户登录,但是现在有个问题,就是用户在没有登录的情况下也可以访问需要认证的页面,比如新增咖啡店页面:

虽然在提交表单数据到后台 API 时不会成功,但是这会给用户造成困扰,所以,我们需要对这种需要认证页面的访问请求进行拦截,我们以前在开发非单页面应用时,会在访问这种页面时从后端进行判断,比如经过 auth 中间件进行过滤,如果需要认证则将用户重定向到登录页面,现在我们可以在前端通过 Vue Router 提供的导航守卫帮助我们快速实现类似 Laravel 中间件的功能。

导航守卫(Navigation Guards)中的导航是一个动词,表示路由正在改变,而守卫指的是在这个改变过程中通过对跳转进行取消或改变来对路由改变进行保护。关于 Vue Router 导航守卫的更多细节,可以访问官方文档:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html,不了解这个功能的话建议你先快速阅读下这篇文档。

在我们这个案例中,我们要实现的功能就是对于需要认证页面的访问,已登录用户可以直接访问,未登录用户会重定向到首页。

第一步:构建 requireAuth() 方法

我们这篇教程将通过路由独享守卫的方式来实现 Vue Router 的导航守卫,并通过 Vuex 来获取用户状态数据,为此需要在 resources/assets/js/routes.js 中引入 Vuex:

import store from './store.js';

然后在 Vue.use(VueRouter); 这一行代码后面定义 requireAuth() 方法来处理需要登录才能访问页面的访问控制,这将是导航守卫所要引用的方法:

function requireAuth(to, from, next) {
    function proceed() {
        // 如果用户信息已经加载并且不为空则说明该用户已登录,可以继续访问路由,否则跳转到首页
        // 这个功能类似 Laravel 中的 auth 中间件
        if (store.getters.getUserLoadStatus() === 2) {
            if (store.getters.getUser != '') {
                next();
            } else {
                next('/home');
            }
        }
    }

    if (store.getters.getUserLoadStatus() !== 2) {
        // 如果用户信息未加载完毕则先加载
        store.dispatch('loadUser');

        // 监听用户信息加载状态,加载完成后调用 proceed 方法继续后续操作
        store.watch(store.getters.getUserLoadStatus, function () {
            if (store.getters.getUserLoadStatus() === 2) {
                proceed();
            }
        });
    } else {
        // 如果用户信息加载完毕直接调用 proceed 方法
        proceed()
    }
}

我们在 requireAuth 方法内部定义了一个 proceed() 方法用于处理路由跳转逻辑,这个方法和 Laravel auth 中间件的 process() 方法实现逻辑非常相似,我们从 Vuex 中获取用户数据来判断用户是否已经登录,如果已经登录继续后续操作,否则将重定向到首页。然后在后面的代码中根据条件调用这个内部函数。

第二步:修改 Vuex 中的 getUserLoadStatus() 方法

需要注意的是,我们在上面的代码中都是通过 store.getters.getUserLoadStatus() 从 Vuex 中获取用户加载状态,这是因为 store.watch 第一个参数只接受函数作为参数,所以需要将 getUserLoadStatus 返回值修改为函数,打开 resources/assets/js/modules/users.js 文件,修改 getters 中的 getUserLoadStatus 方法如下:

getUserLoadStatus( state ){
    return function(){
        return state.userLoadStatus;
    }
},

此外,还要调整其他组件中调用该方法的所有计算属性,包括 resources/assets/js/components/global/Navigation.vueresources/assets/js/pages/Cafe.vue 以及 resources/assets/js/pages/Home.vue,将它们的计算属性 userLoadStatus() 做如下调整:

userLoadStatus() {
    return this.$store.getters.getUserLoadStatus();
},

以前是属性方法名,现在由于属性方法返回值还是函数,所以需要加上()对其进行调用才能获取到状态值。

第三步:在指定路由中使用 requireAuth() 方法

最后,我们在 resources/assets/js/routes.js 的新增咖啡店路由 newcafe 中使用导航守卫,新增一个 beforeEnter 属性,并将其值设置为上面定义的 requireAuth 方法名即可:

{
    path: 'cafes/new',
    name: 'newcafe',
    component: Vue.component('NewCafe', require('./pages/NewCafe.vue')),
    beforeEnter: requireAuth
},

运行 npm run dev 重新编译前端资源,访问 http://roast.test/#/cafes/new,对于未登录用户,就会跳转到首页了:

只有登录用户才能访问新增咖啡店页面,这样,我们就通过 Vue Router 的导航守卫功能在前端实现了认证路由保护。

学院君 has written 1223 articles

Laravel学院院长,终身学习者

积分:163542 等级:P12 职业:手艺人 城市:杭州

1 条回复

  1. Eternity Eternity says:

    学院君,这里的store无法获取到更新后的值,只能获取初始的值

登录后才能进行评论,立即登录?