资讯专栏INFORMATION COLUMN

vue权限管理系统

bovenson / 1013人阅读

摘要:权限系统后台管理系统一般都会有权限模块,用来控制用户能访问哪些页面和哪些数据接口。大多数管理系统的页面都长这样。表为角色权限关联表,一个角色拥有哪些权限是通过这张表查出来的。这样就是一个账号角色权限的关系。

vue权限系统

后台管理系统一般都会有权限模块,用来控制用户能访问哪些页面和哪些数据接口。大多数管理系统的页面都长这样。

左边为菜单,分为两级,右边为图表显示区域,有增删改查的按钮。

表的结构
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_auth_rule
-- ----------------------------
DROP TABLE IF EXISTS `t_auth_rule`;
CREATE TABLE `t_auth_rule` (
  `id_pk` bigint(20) NOT NULL AUTO_INCREMENT,
  `auth_id` varchar(128) NOT NULL COMMENT "权限Id",
  `pauth_id` varchar(128) DEFAULT NULL COMMENT "父级Id",
  `auth_name` varchar(255) NOT NULL COMMENT "权限名称",
  `auth_icon` varchar(255) NOT NULL COMMENT "权限图标",
  `auth_type` smallint(6) NOT NULL COMMENT "权限类型,BIT表示其属性
            0x00表示可显示的菜单权限节点;
            0x01表示普通节点",
  `auth_condition` text COMMENT "条件",
  `remark` varchar(255) DEFAULT NULL COMMENT "备注",
  `is_menu` smallint(255) DEFAULT "0" COMMENT "是否为菜单,0表示非,1表示是",
  `weight` int(11) NOT NULL DEFAULT "0" COMMENT "权重",
  `rule` varchar(256) DEFAULT NULL COMMENT "规则路径主要对应菜单或方法的路径名称",
  `cr_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT "创建时间",
  `up_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT "更新时间",
  PRIMARY KEY (`id_pk`),
  UNIQUE KEY `AK_auth_id` (`auth_id`)
) ENGINE=InnoDB AUTO_INCREMENT=264 DEFAULT CHARSET=utf8 COMMENT="权限规则表,记录权限相关的信息,权限以父子关系存在,菜单是权限的一种。";

SET FOREIGN_KEY_CHECKS = 1;


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_role_auth
-- ----------------------------
DROP TABLE IF EXISTS `t_role_auth`;
CREATE TABLE `t_role_auth` (
  `id_pk` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_id_fk` varchar(32) DEFAULT NULL COMMENT "角色id",
  `auth_id_fk` varchar(128) DEFAULT NULL COMMENT "权限id",
  `aa` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id_pk`)
) ENGINE=InnoDB AUTO_INCREMENT=77 DEFAULT CHARSET=utf8 COMMENT="角色与权限的关系表";

SET FOREIGN_KEY_CHECKS = 1;

稍微解释一下表结构,t_auth_rule 表用来存储对应的权限菜单,一般来说,菜单分为一级和二级菜单,rule字段对应前端的路由规则;而按钮为第三级,rule对应的是接口url地址。
t_role_auth 表为角色权限关联表,一个角色拥有哪些权限是通过这张表查出来的。当然还有一个role表,还有一个账号表,账号表里有一个role的外键。
这样就是一个账号 --> 角色 --> 权限的关系。

对于菜单的权限,通过路由表匹配
// 本地写好路由列表(需要进行动态匹配的)
export const routerList: Array = [
  // 首页
  {
    path: "/",
    name: "_home",
    redirect: "/home",
    component: Layout,
    meta: {
      hideInMenu: true,
      notCache: true
    },
    children: [
      {
        path: "home",
        name: "home",
        meta: {
          hideInMenu: true,
          title: "首页",
          notCache: true,
          icon: "md-home"
        },
        component: () => import("@/views/overview-operations/data-center/DataCenter.vue")
      }
    ]
  }
]

// 负责将后台返回的 菜单列表 转成vue-router所需要的 router list
export function toRouterComponent(menuList: Array) {
  if (!menuList.length) return []

  let routerArr = [];
  for (let j = 0; j < menuList.length; j++) {
    let obj;
    let firstIndex = routerList.findIndex(i => i.path === menuList[j].url); // 一级菜单
    if (firstIndex !== -1) {
      let children: Array = []
      obj = {
        path: routerList[firstIndex].path,
        component: routerList[firstIndex].component,
        redirect: routerList[firstIndex].redirect,
        name: routerList[firstIndex].name,
        meta: routerList[firstIndex].meta,
        children
      };

      // 如果有子菜单
      if (menuList[j].children && menuList[j].children.length) {
        for (let k = 0; k < menuList[j].children.length; k++) {
          const _children = routerList[firstIndex].children!
          let secondIndex = _children.findIndex(i => {
            let fullpath = "";
            if (routerList[firstIndex].path === "/") {
              fullpath = `${routerList[firstIndex].path}${i.path}`;
            } else {
              fullpath = `${routerList[firstIndex].path}/${i.path}`;
            }
            return fullpath === menuList[j].children[k].url;
          });
          if (secondIndex !== -1) {
            obj.children.push(_children[secondIndex]);
          }
        }
      }
    }

    if (obj) {
      routerArr.push(obj);
    }
  }

  return routerArr;
}

// 根据菜单权限,获取路由数组
// 本地只保存后台返回的菜单,在页面刷新的时候从本地拿到菜单重新调用toRouterComponent生成 路由数组
export function getRouterList() {
  if (!storage.get("username")) {
    storage.set("menuTree", ""); // 清空菜单权限数据
  }
  let menuList = storage.get("menuTree") ? storage.get("menuTree") : [];

  const routerArr = toRouterComponent(menuList);
  return routerArr;
}

menuList,菜单数组(或对象), 由后台返回; routerList为前端定义的路由表;遍历routerList,如果routerList的path在menuMap里能找到的话,就表示该路由存在。最后生成一个过滤后的路由表,用vue提供的addRoutes方法动态添加到路由中,并把过滤后的路由表存到本地。

在页面刷新的时候,从本地获取路由表,添加到路由表中,代码如下,constRouterArr为基础路由表,比如登录,404等

注意这一步有个问题,由于我写的storage库用了JSON.stringify,把路由表中的component(实际为一个函数)丢失了,所以在从本地获取路由的时候,还要重新生成一个新的路由表,重新把component加上去,即把上面的addrouters重新执行一遍

对于按钮的权限
if (res.data.auth_rule_map) {
    let obj = {}
    Object.keys(res.data.auth_rule_map).forEach(i => {
      // 将所有的按钮放到一个obj里 key 为接口地址  
      if (res.data.auth_rule_map[i].is_menu === 0) {  // 如果是按钮
        obj[res.data.auth_rule_map[i].rule] = 1
      }             
    })
    storage.set("btnList", obj);
    storage.set("menuTree", res.data.auth_rule_map);
}

auth_rule_map为接口返回权限map,把按钮的权限过滤出来存到本地
将map添加到每个路由组件的data里,(这里有一个问题,怎么判断一个组件是否是路由组件),目前想到的是通过组件name来判断,把所有的路由组件放到一个数组里做判断。

在组件内部的按钮上加上v-if,如果this.uri__里的uri在uriMap里存在就显示。
也可以通过方法来判断,如下面的__isBtnShow,不仅可以控制按钮的显示隐藏,还可以控制其样式,比如颜色等,更加灵活,推荐使用方法来控制

uri = {
    ADD_MEMBER: "/api/add_member"
}

export default function install (Vue) {
  const uriMap = storage.get("btnList")
  //uriMap["/admin/api/auth_rule/update_auth_rule.action"] = 1
  Vue.mixin({
    created() {
      const arr = ["MemberManage", "PayManage", "..."]
      if (arr.indexOf(this.$options.name) !== -1) {
        this.dataUri__ = uriMap
        this.uri__ = uri  
      }
    },
    data() {
      return {
        dataUri__: {}
      }
    },
    methods: {
      __isBtnShow(uri) {
        return uriMap[uri] ? "display: inline-block" : "display: none"
      },
    }
  })
}



// 通过方法来控制,更加灵活
登出的问题

**登出后要清空缓存,routerArr,btnList 等。
由于之前登录,调用addRouter把权限上个账号的路由表加进去了,所以登出后要location.reload()一次,重新实例化路由表,去掉动态添加的路由,只保留基础路由。
location.reload()体验不是太好,但是vue-router没有提供动态删除路由的api,比如 deleteRouter。**

同时登两个账号,导致刷新页面的时候,前者页面的本地缓存被覆盖,权限菜单等数据发生变化,路由表也发生变化

能想到的解决方法是存一个loginIndex 来表示登录账号的个数,比如第一次登录的时候存一个loginIndex=0, 后面存数据的时候都把这个参数带上;后面登多个账号的时候个loginIndex++,这样localStorage的key就是一个动态的(这样还是不行)
最简单的方法是存到localStorage里,只有登出才会清空缓存,只能登一个账号。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101115.html

相关文章

  • vue中如何实现后台管理系统权限控制

    摘要:二接口访问的权限控制接口权限就是对用户的校验。代码如下按扭权限指令至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧五路由控制完整流程图六参考文献手撸后台管理网站之权限控制手摸手,带你用撸后台之权限控制 原文首发于我的博客,欢迎点击查看获得更好的阅读体验~ 一、前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其...

    songze 评论0 收藏0
  • vue中如何实现后台管理系统权限控制

    摘要:二接口访问的权限控制接口权限就是对用户的校验。代码如下按扭权限指令至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧五路由控制完整流程图六参考文献手撸后台管理网站之权限控制手摸手,带你用撸后台之权限控制 原文首发于我的博客,欢迎点击查看获得更好的阅读体验~ 一、前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其...

    iliyaku 评论0 收藏0
  • vue中如何实现后台管理系统权限控制

    摘要:二接口访问的权限控制接口权限就是对用户的校验。代码如下按扭权限指令至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧五路由控制完整流程图六参考文献手撸后台管理网站之权限控制手摸手,带你用撸后台之权限控制 原文首发于我的博客,欢迎点击查看获得更好的阅读体验~ 一、前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其...

    VincentFF 评论0 收藏0
  • Vue项目实现简单的权限控制

    摘要:路由不可见实现方法在中的字段中加入该路由的访问权限列表字段。元素不可见实现方法因为某些页面中会有一些特殊的接口调用或数据展示受到权限控制显示。 在Vue项目中实现权限控制管理 对于一般稍大一些的后台管理系统,往往有很多个人员需要使用,而不同的人员也对应了不同的权限系统,后端的权限校验保障了系统的安全性,而前端的权限校验则提供了优秀的交互体验。 校验方式 前端对用户的权限信息进行校验往往...

    fou7 评论0 收藏0
  • Vue项目实现简单的权限控制

    摘要:路由不可见实现方法在中的字段中加入该路由的访问权限列表字段。元素不可见实现方法因为某些页面中会有一些特殊的接口调用或数据展示受到权限控制显示。 在Vue项目中实现权限控制管理 对于一般稍大一些的后台管理系统,往往有很多个人员需要使用,而不同的人员也对应了不同的权限系统,后端的权限校验保障了系统的安全性,而前端的权限校验则提供了优秀的交互体验。 校验方式 前端对用户的权限信息进行校验往往...

    chaosx110 评论0 收藏0
  • sSpring Boot多模块+ Shiro + Vue:前后端分离登陆整合,权限认证(一)

    摘要:前言本文主要使用来实现前后端分离的认证登陆和权限管理,适合和我一样刚开始接触前后端完全分离项目的同学,但是你必须自己搭建过前端项目和后端项目,本文主要是介绍他们之间的互通,如果不知道这么搭建前端项目的同学可以先找别的看一下。 前言 本文主要使用spring boot + shiro + vue来实现前后端分离的认证登陆和权限管理,适合和我一样刚开始接触前后端完全分离项目的同学,但是你必...

    macg0406 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<