vue后台管理系统之权限控制

目录

 

什么是权限控制?

如何实现权限控制

改进的解决方案

基本实现

登出再登入问题

页面刷新问题


什么是权限控制?

首先,我们按照最初的开发模式,在router.js中将所有的组件都注册在路由中.

import Vue from 'vue'
import Router from 'vue-router'
import { extend } from '../utils/router'

Vue.use(Router)
const router = new Router({
  routes: [
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/login')    //懒加载
    },
    {
      path: '/home',
      name: 'home',
      roles: ['guest', 'admin'],    //用于验证路由能够被访问的角色
      component: () => import('../views/home')
    }
    ...
    {
      path: '*'
      redirect: '/404'
    }
  ]
})

export default router

然后你可能会在登录成功后,可以从后台返回的数据中得到用户的权限,通过这个权限,控制显示的导航菜单.

比如从后台接受到的数据是这样的:

{
    code: 200,
    ...
    rows: [
        {
            userCode: AKJUFIANJ,
            ...
            roles: 'admin'
        }
    ]
}

然后把用户信息存储到vuex当中去,通过判断用户的角色,得到导航菜单.

或许你会这样做:

//  导航菜单
    {{item.name}}
computed { menu () { let menuArray = [] // 用户为消费者,返回商城等导航 if (store.getter.roles === 'guest') { menuArray = [ { name: '商城', url: '/shop' }, ... ] } // 用户为管理员,返回管理等导航 else if (store.getter.roles === 'admin') { menuArray = [ { name: '管理', url: '/manage' }, ... ] } return menuArray } }

问题来了:

假如,我直接访问/manage,就能进入管理员路由页面了.所以这就需要权限控制,限制用户进入禁止访问的路由.

 

如何实现权限控制

 

我们接着上面讲,那么如果进行权限控制?

我相信你以前一定这么做过:

main.js:

router.beforeEach((to, from, next) => {
  to.roles.includes(store.getter.roles) ? next() : next('/login')
})

通过beforeEach在每次路由跳转的时候判断我们登录成功后存储在vuex中的角色是否包含在目标路由可访问的角色当中.

这样做,虽然能够达到效果.但是如果我将vuex当中的roles值更改,一样能够突破浏览器控制,访问到权限控制路由页面.

千万不要以为这种事做不到,要知道,几乎所有存储在浏览器的数据用户都是能够访问到的.如果你将权限控制完全交给前端,恶意用户很容易就能够突破浏览器,对系统造成危害.

因此,这种方式是行不通的,我们需要确保每次访问的路由都是经过授权的.

 

改进的解决方案

使用addRoutes动态添加路由,通过登录之后后台返回的数据动态加载路由,给予能够访问的路由页面.这样一来,即使访问权限控制页面,由于并没有注册到路由当中,也是无法访问的.

补充:

目前vue当中进行权限控制的方案有两种:

第一种:前端进行权限逻辑控制 (路由数据在前端,根据后台返回数据进行选择性动态加载)

第二种:后台进行权限逻辑控制 (路由数据由后台返回给前端进行动态加载)

本文探讨的是第二种实现方式,其实两种方式思想是一致的,只要能够实现一种,另外一种方式的实现也会信手拈来.

 

基本实现

假设后台返回的数据如下:

{
        code: 200,
        token: 'FSDFD21321GVXZV31243',
        routes:
        {
          path: '/',
          component: 'XXX',
          children:
          [
            {
              path: 'home',
              name: '首页',
              component: 'home'
            },
            {
              path: 'shop',
              name: '购物',
              component: 'shop'
            },
            {
              path: 'pay',
              name: '支付',
              component: 'pay'
            }
          ]
        }
}

在登录成功后,将json转换为实例化router时的routes数组.也就是一下形式(具体实现略)

[
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/login')
    },
    ...
]

然后使用注册动态路由

// extendsRouter为将json转换为数组的函数
this.$router.addRoutes(extendsRouter(res.roles).concat({
    path: '*',
    redirect: '/404'
}))

最后我们需要将错误的路由页面重定向到404.

 

这样就完了吗?当然没有

我们之前说过,权限控制不能完全交给前端,因为浏览器防御很容易被突破(对于懂web的人来说相当于没有防御力),因此我们在每次路由跳转的时候都需要访问后台,通过后台返回的数据判断目标路由是否在权限内,而不是将目标路由与存储在前端的路由做判断。

因此,beforeEach中我们需要改善一下我们的代码:

//  伪代码

router.beforeEach((to, from, next) => {
  //  跳往基本路由(白名单路由)不进行拦截
  if (isBaseRouter(to.path)) {
    next()
    return false
  }
  //  如果没有token则跳转到登录页
  else if (!store.getter.token)  {
    next('/login')
    return false
  }

  //  如果有token,发起请求获取路由信息
  //  如果返回的路由信息里包括了目标路由,则放行
  //  request.get('/routes').includes(to.path) ? next() : next('/login')
  if ( request.get('/routes').includes(to.path) ) {
    next()
  }
  //  如果目标路径不在权限内.那么就必须重新登录注册动态路由后才能够访问
  else {
    next('/login')
  }
})

补充:

使用router.addRoutes添加的动态路由并不是响应式的,也就是说在router.options.routes当中没有动态添加的路由,存放的是静态路由.isBaseRouter其实就是目标路径(to.path)是否存在于router.options.routes当中

另外,根据实际情况还需要对token失效问题进行处理,略.

 

做到这儿,权限控制也差不多了,但是还有一些小问题.

 

登出再登入问题

心思缜密的你一定已经意识到了:如果A和B是两个不同的权限用户,只能访问各自的路由页面.那么现在A注销账号,清空数据后,用B账号登录,由于路由未实例化的原因,在我们登录了A和B之后,动态路由当中注册的路由就已经包含了A和B的权限内路由.

虽然我们通过beforeEach可以控制访问权限,但是路由规则中却存在权限外的路由.这样不太严谨,而且也可能出现安全问题.

因此,我们需要在登录成功,添加动态路由前,清除动态添加的路由.

 

但是,目前为止我并没有查到相关资料能够清除动态添加的路由,如果哪位大佬知道,恳请在评论中告知一下,大家一起学习.

 

既然没有办法清除动态添加的路由,那么就重新实例化.

1. 重新加载页面

2. 重新将当前路由重新实例化 new Router()

第一种方法那么就是在退出的时候location.reload().但是SPA的话, 首次加载速度一直是一个棘手的问题, 如果没有什么影响的话,项目不大, 可以采取这种形式, 但不推荐.

第二种方法则是在addRoutes之前,将当前router实例重新实例化

// 给Router原型链注册reset函数以重新实例化
// 以便重新生成的Router实例也能够进行重新实例化

import Vue from 'vue'
import Router from 'vue-router'

Router.prototype.reset = function () {
  var that = this
  that = new Router({
    routes: that.options.routes  //  新的路由也需要包含静态路由
  })
}

Vue.use(Router)

const router = new Router({
  routes: [...]
})


export default router


Login.vue:

methods: {
  handleLogin () {
    ...
    //后台返回登录成功后
    this.$router.reset()
    this.$router.addRoutes(...)
  } 
}

好了,这样一来登入登出就完了

 

页面刷新问题

按照以上的思路,权限控制基本上就完成了,但是还有个棘手也是必须解决的问题:页面刷新

假如我在浏览权限路由页面当中,不小心碰到了f5, 那么整个路由重新实例化, 这个时候能通过权限控制, 但是路由并没有注册. 因此我是无法访问到权限控制的路由页面.

那么我的解决思路是:

在main.js中获取路由权限数据并动态注册,这样一来重新刷新的时候, 就能匹配到相对应的路由

//  伪代码

router.addRoutes(req.get('/routes'))

 

分享一下写的比较好的文章:

用addRoutes实现动态路由

vue实现登录权限

 

你可能感兴趣的:(vue,Javascript)