页面按钮权限控制

634 阅读4分钟

1.这节我们先做操作,再来理清下原理,我们还是拿登录日志来做操作,我们的若依后台有两个登录账号admin和ry,默认admin是超级管理员,登录日志有个新建按钮,我们要实现用admin登录能看到新建按钮,而用ry登录,看不到新建按钮的效果。 ​

2.由于之前我们设置了/list/loginfo不能在地址栏中直接输入访问,我们现在要用登录日志做示例,就要放开权限,我们到config/config.js中注销掉登录日志的路由中的 // access: 'authorize',这行,注释后的登录日志完整路由配置为

   {
          name: 'loginfor',
          icon: 'smile',
          path: '/list/loginfo',
          component: './loginfo',
          // access: 'authorize',
     },

3.在src/utils目录下新建permission.js文件,完整的permission.js代码如下

export function matchPermission (permissions, value) {

  if(permissions === undefined)
    return false;
  const type = typeof value;
  if (type === 'string') {
    return matchPerm(permissions, value);
  }
  return matchPerms(permissions, value);
}

// /**
//  * 字符权限校验
//  * @param {Array} value 校验值
//  * @returns {Boolean}
//  */
export function matchPerms (permissions, value) {
  if (value && value instanceof Array && value.length > 0) {
    const permissionDatas = value;
    const all_permission = '*:*:*';
    const hasPermission = permissions.some((permission) => {
      return all_permission === permission || permissionDatas.includes(permission);
    });
    if (!hasPermission) {
      return false;
    }
    return true;
  }
  console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`);
  return false;
}

export function matchPerm (permissions, value) {

  if (value && value.length > 0) {
    const permissionDatas = value;
    const all_permission = '*:*:*';
    const hasPermission = permissions.some((permission) => {
      return all_permission === permission || permissionDatas === permission;
    });
    if (!hasPermission) {
      return false;
    }
    return true;
  }
  console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`);
  return false;
}

/**
 * 角色权限校验
 * @param {Array} value 校验值
 * @returns {Boolean}
 */
export function checkRole (roles, value) {
  if (roles && value && value.length > 0) {
    for(let i = 0; i< roles?.length; i ++) {
      for(let j = 0; j< value?.length; j ++) {
        if(value[j] === roles[i].roleKey) {
          return true;
        }
      }
    }
  }
  console.error(`need roles! Like checkRole="['admin','editor']"`);
  return false;
}

4.修改app.jsx中的fetchUserInfo代码,主要改下返回的结果,修改后的fetchUserInfo代码如下

  const fetchUserInfo = async () => {
    try {
      const msg = await queryCurrentUser();
      const currentUser={...msg.user,permissions: msg.permissions};
      console.log("login user info:",currentUser);
      return currentUser;
    } catch (error) {
      history.push(loginPath);
    }
    return undefined;
  };

这里我们在fetchUserInfo中加入了, msg.permissions,也就是在全局初始数据的currentUser中会加入从后台拿到的当前用户的权限列表,超管的permissions是['::*'],ry的permissions是

[
'system:user:resetPwd',
 'system:post:list',
'monitor:operlog:export',
'monitor:druid:list',
 'system:menu:query', 
 'system:dept:remove', 
 'system:menu:list', 
 'tool:gen:edit', 
 'system:dict:edit', 
 ... //此处省略若干权限值
 ]

这个permissions是个数组,代表当前登录用户有权操作的标识列表。超管的['::*']代表啥都可以操作。 ​

5.打开src/access.js,先引入第3步中权限判断的方法

 import { matchPermission } from "./utils/permission";

6.在access.js的access 函数的return结果中增加一些代码。完整的access函数代码如下

export default function access(initialState) {

  const { currentUser, menuData } = initialState || {};
  return {
    hasPerms: (perm) => {
      return matchPermission(currentUser?.permissions, perm);
    },
    hasNoPerms: (perm) => {
      return !matchPermission(currentUser?.permissions, perm);
    },

    authorize: (route) => {
      if(menuData) {
        const items = getMatchMenuItem(route.path, menuData);
        if(!items || items.length === 0){
          return false;
        } else {
          return true;
        }
      }
      return true;
    }, // initialState 中包含了的路由才有权限访问
  };
}

其中authorize是上次我们做路由和菜单权限控制时用的,这里我们加了两个key hasPerms,hasNoPerms从字面意义就可以判断hasPerms是判断是不是有权限,返回true为有权限, hasNoPermss是判断没有权限,返回true为没有权限。 ​

7.打开登录日志操作的列表页面src/pages/loginfo/index.jsx,我们要判定的新建按钮在这个jsx文件里面,先import一段代码,在页面开头最后一行的impor后新起一行,加一个import

import { useAccess } from 'umi';

8.在const TableList = () => { 函数中的,第一行加一行代码 const access = useAccess();

const TableList = () => {
  const access = useAccess();
  ......

9.找到新建按钮的代码位置

    toolBarRender={() => [
          <Button
            type="primary"
            key="primary"
           
            onClick={() => {
              handleModalVisible(true);
            }}
          >
            <PlusOutlined /> 新建
          </Button>,
        ]}

在Button上增加一行代码 hidden={access.hasNoPerms('system:loginfo:add')}

 toolBarRender={() => [
          <Button
            type="primary"
            key="primary"
            hidden={access.hasNoPerms('system:loginfo:add')}
            onClick={() => {
              handleModalVisible(true);
            }}
          >
            <PlusOutlined /> 新建
          </Button>,
        ]}

这里的system:loginfo:add是我们自定义的一个权限标识,标识对登录日志的新增权限,这个权限超管的permision中没有但是配置了['::*'],超管他是有权限的,而ry的permision列表里面肯定没有这个system:loginfo:add权限标识,所以这个按钮超管可见,而ry不可见。 ​

10.保存前端代码,编译后先用admin登录,然后在地址栏中输入list/loginfo看是否能见到新建按钮,截图如下 image.png

11.切换ry用户登录,然后再地址栏中如如list/loginfo,看能否见到新建按钮,截图如下 image.png 通过当前的登录用户的权限列表值,permissions可以控制页面按钮级别的显示、隐藏,这个permission的值,我们在后台可以配置。这样就可以实现页面按钮级别的权限控制了。 ​

12.做完操作,看到效果,我们再总结下原理,我们可以从后台获取到当前用户的permissions,将其写入全局初始数据currentUser中,然后在access.js中可以配置是否有权的key hasPerms或者hasNoPerms,传入一个当前的操作权限值如system:loginfo:add,根据currentUser中当前用户的权限列表permissions,我们可以判断当前用户是否有system:loginfo:add的操作权限,根据权限值真假,来控制按钮显示或者隐藏,permissions后台可配,另外如果要实现根据不同角色控制按钮的权限,原理类似。 ​ 最近做了个小API应用,希望大家关注支持下: www.yuque.com/docs/share/…