权限理解及vue实现

权限控制归根到底是用来控制对资源的访问,它需要前后端共同来实现。

一、后端实现

从后端来说,权限控制的对象一般是接口,有时候会细致到某个具体的方方法,核心就是保护对底层数据库的访问。

1.模型设计

一般涉及如下几张表,包括user表、role表(也可以理解为拥有一系列权限的group)和permission表,

user_role和role_permission一般是为了实现用户和组,以及组和权限的多对多灵活控制的辅助表,

就我个人理解,如果更核心一些,其中只需要两张表:user表和permission表,role表的出现更多的是因为权限粒度很小的时候,一个用户会对应很多权限,一个一个的开非常繁琐,对一系列权限进行归纳整合成一个角色,更方便管理。

CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `role` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user_role` (
`user_id` bigint(11) NOT NULL,
`role_id` bigint(11) NOT NULL
);
CREATE TABLE `role_permission` (
`role_id` bigint(11) NOT NULL,
`permission_id` bigint(11) NOT NULL
);
CREATE TABLE `permission` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(255) NULL,
`pid` bigint(11) NOT NULL,
PRIMARY KEY (`id`)
);

有了这些表之后,在后端就可以设计全局的拦截器,对权限和路由进行判断,没有权限的访问会被直接拒绝或者跳转。

2.方法保护

spring security还有一些更细节的控制,会采用注解的方式加在service层的方法上,避免有人突破了URL层的限制。显然这样可以更好的保护对数据层的访问。

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
class A{
@Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public void createModel(String tbName, List<Columns> columnsList) {
        saveModelColumns(columnsList);
        createDataTable(tbName);
    }

    @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')")
    public void saveModelColumns(List<Columns> columnsList) {
        columnsDao.batchInsertColumns(columnsList);
    }

}

二、前端实现

前端的权限控制五花八门,在我看来,大概是通过如下三个方面来实现的。

1.通过路由跳转时的拦截控制用户对具体页面的访问

router.beforeEach((to, from, next) => {
  const auth = store.state.user.authority;
  const token = store.state.user.token;
  /*1.去登录页没有权限要求;
    2.去登录页之外的页面要判断是否登录,未登录先跳转到登录页;
    3.如果已登录则判断是否有权限,没权限则返回403;
    4.权限验证通过,直接放行
    */
  if (to.name === "Login") {
    next();
  } else if (!token || token === "") {
    next({ name: "Login" });
  } else if (to.meta.authority && !auth.includes(to.meta.authority)) {
    next({ name: "Forbidden" });
  } else {
    next();
  }
});

2.通过对按钮的显示隐藏控制用户对具体内容的编辑

这个主要通过自定义指令实现

user.js用户权限缓存

const state = () => ({
  username: "",
  token: "123",
  authority:['add','edit','delete','chart_view'],
});

main.js

app.directive('auth', {
    mounted(el, binding) {
        if (!store.state.user.authority.some(item=> item===binding.value)) {
            el.parentNode && el.parentNode.removeChild(el)
        }
    }
});

*.vue

  <el-button v-auth="'add'"  size="mini" type="primary" plain  @click="handleCreate">添加</el-button>

只有拥有add权限的用户才能看见这个添加按钮

3.通过菜单项的显示隐藏控制用户对具体页面的访问

这个主要用于提升用户体验,如果用户没有权限也能看到某个菜单,一点就是403,体验会很差,还不如不让看见。

在通过route.js菜单渲染的时候,加入权限的过滤,就可以实现不同用户看到不同的菜单栏

methods: {
    //渲染菜单时根据当前用户的权限排除当前用户没有权限的菜单
    getLinks() {
      let rt = this.$router.options.routes;
      rt.forEach((item) => {
        if (item.path === "/doc") {
          this.linkData = [];
          item.children.forEach((child) => {
            if (child.name) {
              const auth = this.$store.state.user.authority;
              if (child.meta) {
                if (auth.includes(child.meta.authority)) {
                  this.linkData.push(child);
                }
              } else {
                this.linkData.push(child);
              }
            }
          });
          
        }
      });
    },
  },

当系统要求对权限做特别细粒度的控制时,可以采用用户-权限这种模式,如果要求不是那么高,推荐使用用户-角色这种比较粗粒度的实现。