木灵鱼儿

木灵鱼儿

阅读:568

最后更新:2020/12/06/ 2:29:25

如何使用路由管理用户权限

以前弄个一个路由鉴权,其原理就是判断用户是否已经登录,未登录就跳转至登录页。

通过给meta设置一个元数据,然后判断这个页面是否有权限,如果访客没有权限,就跳转至登录页。

这个用于一般情况完全是足够了,但是当你的网站权限分级的情况,就不太够用了,因为已经不能用单纯的是否已登录来判断,访客时候有权限浏览该页面。

多级权限

假设我们网站有四个等级:master(站长)、admin(管理员)、user(注册用户)、guest(游客)

那么最先,我们给路由设置权限,设置哪些等级可以访问

export default [
  {
    path: "", component: () => import(""),
    meta: {
      authority: ["master", "admin", "user"],
      title: "首页"
    }
  },
  {
    path: "/admin", component: () => import(""),
    meta: {
      authority: ["master", "admin"],
      title: "管理页"
    }
  },
  {
    path: "/user", component: () => import(""),
    meta: {
      authority: ["user"],
      title: "个人中心"
    }
  },
  {
    path: "/login", component: () => import(""),
    meta: {
      authority: ["guest"],
      title: "登录页"
    }
  }
]

我们有四个页面,每个页面都有不同的浏览器权限。因为有多个层级权限,所以authority是一个数组。

现在原数据有了,我们怎么进行判断呢?

判断是否有权限

这个就很多花样了,每个公司的方式都不一样,这里简单讲一个例子:

后端提供一个鉴权接口,我们可以在进入路由前先请求一次接口,然后获取后端提供的权限。

function getAuthority() {
  return "admin";
}

这里就简单模拟下api,最终返回的是一个权限字符串。

function check(metaAuthority) {
  const current = getAuthority();
  return  metaAuthority.includes(item);
}

check接收一个参数,这个参数就是路由的meta.authority,他是一个数组,我们先获取到后台返回的权限字符串,然后判断meta.authority数组中是否存在对应的值,如果有,说明这个人有权限访问该页面。

路由守卫

那么我们可以直接在路由守卫里这样写

router.beforeEach((to, from, next) => {
  if (!check(to.matched.some(r => r.meta.authority))) {
    //没有权限访问
  }
  next();
});

没有权限访问有两种情况,一种是用户没有登录,一种是已登录,但是没权限。

所以我们还需要判断下是否登录

function isLogin() {
  const current = getAuthority();
  return current.every(r => r !== "guest");
}

遍历后端返回的权限数组,然后判断,只要有一个不满足条件,就返回false,所有的遍历条件都通过,返回true。

router.beforeEach((to, from, next) => {
  if (!check(to.matched.some(r => r.meta.authority))) {
    //未登录
    if (!isLogin() && to.path !== "/login") {
      next({ path: "/login" });
    } else if (to.path !== "403") {
      // 已登录但是没有权限
      next({ path: "/403" });
    }
  };
  next();
});

用户没有登录就跳转到登录页,由于登录页页是未登录状态,所以防止重复跳转,要加个判断,path不等于login

如果已经登录了,就说明这个人没有权限访问,我们就跳转403错误页,由于也要防止403重复跳转,加个path判断。

需要注意的是,使用了next跳转,不会触发路由的afterEach守卫函数,所以,如果有在beforeEach时调用的等待函数,如进度条效果,那么,使用next后需要手动触发结束,因为不会再进入到afterEach钩子中去了。

导航菜单鉴权

既然用户没权限访问这个页面,最好的办法就是让用户无法点击进入到这个页面,没有入口,体验就好很多,就好像,我这有块肉,你就是吃不到,既然吃不到,当然是眼不见为净了。

已上一章为例,在使用的getMenuData方法获取导航菜单数据的时候,我们可以进行操作,如果用户没有权限,那么就不插入这个路由

getMenuData(routes = []) {
      const menuData = [];
      //鉴权
      for (let route of routes) {
        if (
          route.meta &&
          route.meta.authority &&
          !check(route.meta.authority)
        ) {
          //跳出本次循环
          break;
        }
        //鉴权结束
        if (route.name && !route.hideInMenu) {
          const newRoute = { ...route }; //解构赋值到新变量上
          delete newRoute.children; //删除children,因为需要判断
          //是否有子级,并且显示
          if (route.children && !route.hideChildrenInMenu) {
            //递归并赋值
            newRoute.children = this.getMenuData(route.children);
          }
          //一个判断递归结束
          menuData.push(newRoute);
        } else if (
          !route.hideInMenu &&
          !route.hideChildrenInMenu &&
          route.children
        ) {
          //有子级,但是不隐藏自己,也不隐藏子级
          //拿到子级,并将其提升层级,丢到外面
          menuData.push(...this.getMenuData(route.children));
        }
      }
      return menuData;
    }

我们将forEach循环改为for of循环,这样就可以使用break跳出本次循环。

然后if进行判断,如果这个路由meta存在,并且authority也存在,就判断用户是否有权限访问该页面。

如果没有直接break跳出,这样数据就不会插入到数组中去。

如果存在就按照原来的进行递归处理。

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

关于作者

站点职位 博主
获得点赞 0
文章被阅读 568

相关文章