你好,我是大圣。

在上一讲中,我们使用组件库完成了项目的搭建,并且引入了 Element3 和 axios 等基础库。基于 Element3,我们可以很方便地使用组件搭建项目。而使用 axios,可以很方便地获取后端数据。在项目中,权限系统的控制需要前后端配合完成,而且权限系统也是后端管理系统中常见的一个难点。不过,今天我们主要从前端的角度,来聊一下项目的权限系统。

下面,我们先从登录权限谈起,因为登录权限对于一个项目来说是必备的功能模块。完成了登录选项的设置后,下一步需要做的是管理项目中的页面权限,而角色权限在这一过程中则可以帮助我们精细化地去控制页面权限。

登录权限

继续上一讲我们搭建起来的项目,你可以看到现在所有的页面都可以直接访问了,通常来说管理系统的内部页面都需要登录之后才可以访问,比如个人中心、订单页面等等。首先,我们来设计一个这样的权限限制功能,它能保证某些页面在登录之后才能访问。

为了实现这个功能,我们首先需要模拟登录的接口和页面。我们先新增路由页面,进入到项目目录下,在 router.js 中新增路由配置。下面的代码中,routes 数组新增 /login 路由访问。

import Login from '../components/Login.vue'
  const routes = [
  ...
  {
    path: '/login',
    component: Login,
    hidden: true,
  }
  ]

然后,我们进入到 src/components/Login.vue 组件中,组件的代码如下所示。在代码中,我们能看到,用户在输入用户名和密码之后,把用户名和密码传递给后端,然后就可以实现登录认证。

handleLogin() {
  formRef.value.validate(async valid => {
    if (valid) {
      loading.value = true
      const {code, message} = await useStore.login(loginForm)
      loading.value = false
      if(code===0){
        router.replace( toPath || '/')
      }else{
        message({
          message: '登录失败',
          type: 'error'
        })
      }
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

由于我们的项目是一个前端项目,所以我们需要在 Vite 内部做数据结构的模拟。我们在 src 目录下面新建 mock 目录,用来放置假数据的结构。我们写死一个用户名 dasheng,使用用户名 dasheng 登录成功之后,我们把用户名、过期日期等重要信息进行加密,生成一个 token 返回给前端。

这个 token 就算是一个钥匙,对于那些需要权限才能读取到的页面数据,前端需要带上这个钥匙才能读取到数据,否则访问那些页面的时候就会显示没有权限

{
    url: '/geek-admin/user/login',
    type: 'post',
    response: config => {
      const { username } = config.body
      const token = tokens[username]

      // mock error
      if (user!=='dasheng') {
        return {
          code: 60204,
          message: 'Account and password are incorrect.'
        }
      }

      return {
        code: 20000,
        data: token
      }
    }
  }

我们回到前端页面,登录成功后,首先需要做的事情,就是把这个 token 存储在本地存储里面,留着后续发送数据。这一步的实现比较简单,直接把 token 存储到 localStorage 中就可以了。我们拿到这个 token 后,为了进行接口权限认证,要把 token 放在 HTTP 请求的 header 内部。

我们看下面的代码,在 axios 的请求发出之前,我们在配置中使用 getToken 从 localStorage 中读取 token,放在请求的 header 里发送。由于我们使用了请求拦截的方式,所以所有的后端数据发送的时候,都会带上这个 token,完成受限数据的请求。

service.interceptors.request.use(
  config => {
    const token = getToken()
    // do something before request is sent
    if (token) {
      config.headers.gtoken = token
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

通过上面的操作,我们完成了前端网络请求的 token 限制。但是还有一个需求没有实现,就是用户没有登录某个受限页面的时候,或者说没有 token 的时候,如果直接访问受限页面,比如个人中心,那么就需要让 vue-router 拦截这次页面的跳转。

与 vue-router 拦截页面的跳转,并显示无权限的报错信息相比,直接跳转登录页是现在更流行的交互方式。但这种方式需要在 vue-router 上加一层限制,这层限制就是说,在路由跳转的时候做权限认证,我们把 vue-router 的这个功能称作导航守卫。

关于导航守卫的 API,你可以从 Vue Router 的官网看到很详细的介绍。这里我们实际应用下,下面的代码中,我们在 router.beforeEach 函数中设置一个全局的守卫。

每次页面跳转的时候,vue-router 会自动执行守卫函数,如果函数返回 false 的话,页面跳转就会失败。不过,我们也可以手动地跳转到其他页面。现在我们设置的路由很简单,如果 token 不存在的话直接跳转登录页面,否则返回 true,页面正常跳转。