前后端配合实现按钮级操作权限控制

背景

公司项目需要做到按钮级权限限制,至此有了该文,如有错误,请联系博主指出,多多感谢。

角色配置前后端操作

首先最基本的角色配置,配置该类角色有哪些菜单以及那些菜单的哪些按钮权限

菜单及菜单按钮由前端维护(或者也可以后端数据库维护)

前端维护一个JSON文件,直接读取渲染页面即可

JSON文件类似这样,定义菜单及菜单下按钮,声明唯一key和name(角色配置时需要存储对应菜单及按钮的key)

json 复制代码
[
  {
    key: 'Home',
    menu: '首页',
  },
  {
    key: 'OrgManagement',
    menu: '组织管理',
    children: [
      {
        key: 'RoleConfig',
        menu: '角色信息配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'PersonnelConfig',
        menu: '人员信息配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'TeamsConfig',
        menu: '班组信息配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
    ],
  },
  {
    key: 'FacilityManagement',
    menu: '设施管理',
    children: [
      {
        key: 'LineManagement',
        menu: '线体管理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'DeviceManagement',
        menu: '设备管理',
        children: [
          {
            key: 'DeviceManagement_DeviceType',
            menu: '设备类型',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
          {
            key: 'DeviceManagement_Device',
            menu: '设备',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
        ],
      },
      {
        key: 'ComponentManagement',
        menu: '部件管理',
        children: [
          {
            key: 'ComponentManagement_ComponentType',
            menu: '部件类型',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
          {
            key: 'ComponentManagement_Component',
            menu: '部件',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    key: 'OamAlarm',
    menu: '运维报警',
    children: [
      {
        key: 'AlarmTemplate',
        menu: '报警模版',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'AlarmRecords',
        menu: '报警明细',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'ignore',
            name: '忽略',
          },
          {
            key: 'createOrder',
            name: '生成工单',
          },
          {
            key: 'oneKeyHandle',
            name: '一键处理',
          },
        ],
      },
      {
        key: 'AlarmWorkOrderRecords',
        menu: '工单明细',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'receive',
            name: '接单',
          },
        ],
      },
      {
        key: 'AlarmLevel',
        menu: '报警等级配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
    ],
  },
  {
    key: 'OamPatrol',
    menu: '运维巡检',
    children: [
      {
        key: 'PatrolTaskConfig',
        menu: '巡检任务配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'FrequencyRulesConfig',
        menu: '频率规则配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'DistributeRulesConfig',
        menu: '下发规则配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'PatrolTaskManagement',
        menu: '巡检任务处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'distribute',
            name: '下发',
          },
          {
            key: 'handle',
            name: '处理',
          },
        ],
      },
    ],
  },
  {
    key: 'Maintenance',
    menu: '维修保养',
    children: [
      {
        key: 'MaintenanceDeviceConfig',
        menu: '维保设备配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'MaintenancePlanConfig',
        menu: '维保计划配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'MaintenanceRemindConfig',
        menu: '维保提醒配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'MaintenanceTaskManagement',
        menu: '维保任务处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'distribute',
            name: '下发',
          },
        ],
      },
    ],
  },
  {
    key: 'SpareParts',
    menu: '备品备件',
    children: [
      {
        key: 'WarehouseConfig',
        menu: '库房库位配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'SpareTypeConfig',
        menu: '备件类型配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'SpareAccountManagement',
        menu: '备件台帐管理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'PurchaseApply',
        menu: '采购申请',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'examine',
            name: '审核',
          },
          {
            key: 'createPurchaseOrder',
            name: '生成采购单',
          },
        ],
      },

      {
        key: 'ArrivalManagement',
        menu: '到货处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'confirm',
            name: '到货确认',
          },
          {
            key: 'entry',
            name: '生成入库',
          },
        ],
      },

      {
        key: 'EntryStorageManagement',
        menu: '入库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'entry',
            name: '入库',
          },
        ],
      },

      {
        key: 'ExitStorageApply',
        menu: '出库申请',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'examine',
            name: '审核',
          },
        ],
      },

      {
        key: 'ExitStorageManagement',
        menu: '出库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'exit',
            name: '出库',
          },
        ],
      },

      {
        key: 'ExitEntryStorageRecords',
        menu: '出入库记录',
      },

      {
        key: 'StocktakingManagement',
        menu: '盘库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'stocktaking',
            name: '盘库',
          },
        ],
      },

      {
        key: 'TransferManagement',
        menu: '调库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'handle',
            name: '处理',
          },
        ],
      },
    ],
  },
  {
    key: 'Dict',
    menu: '数据字典',
  },
  {
    key: 'Knowledge',
    menu: '知识库',
    checkPermissions: [],
    permissionOptions: [
      {
        key: 'add',
        name: '新增',
      },
      {
        key: 'edit',
        name: '编辑',
      },
      {
        key: 'delete',
        name: '删除',
      },
    ],
  },
]

根据上述JSON文件,渲染出的角色权限编辑页面如下:

该页面可以配置角色菜单及按钮权限,前端请求参数如下:

json 复制代码
{
  "roleName": "库管员",
  "roleSort": 1,
  "remark": "库管员",
  "menuIds": [
    "SpareParts",
    "WarehouseConfig",
    "SpareTypeConfig",
    "SpareAccountManagement",
    "PurchaseApply",
    "ArrivalManagement",
    "EntryStorageManagement",
    "ExitStorageApply",
    "ExitStorageManagement",
    "ExitEntryStorageRecords",
    "StocktakingManagement",
    "TransferManagement"
  ],
  "permissions": [
    "WarehouseConfig/add",
    "WarehouseConfig/edit",
    "WarehouseConfig/delete",
    "SpareTypeConfig/add",
    "SpareTypeConfig/edit",
    "SpareTypeConfig/delete",
    "SpareAccountManagement/add",
    "SpareAccountManagement/edit",
    "SpareAccountManagement/delete",
    "PurchaseApply/add",
    "PurchaseApply/edit",
    "PurchaseApply/delete",
    "PurchaseApply/examine",
    "PurchaseApply/createPurchaseOrder",
    "ArrivalManagement/confirm",
    "ArrivalManagement/entry",
    "EntryStorageManagement/add",
    "EntryStorageManagement/edit",
    "EntryStorageManagement/delete",
    "EntryStorageManagement/entry",
    "ExitStorageApply/add",
    "ExitStorageApply/edit",
    "ExitStorageApply/delete",
    "ExitStorageApply/examine",
    "ExitStorageManagement/add",
    "ExitStorageManagement/delete",
    "ExitStorageManagement/exit",
    "StocktakingManagement/add",
    "StocktakingManagement/edit",
    "StocktakingManagement/delete",
    "StocktakingManagement/stocktaking",
    "TransferManagement/add",
    "TransferManagement/edit",
    "TransferManagement/delete",
    "TransferManagement/handle"
  ]
}

后端需要将该角色有的菜单权限及按钮权限存起来,存在sys_role表中,表结构如下:

主要关注划红线的两个字段,将上述请求参数,分别用逗号隔开存与这两个字段(菜单以及按钮权限)中,此时角色菜单级及按钮级权限以维护好!(此时可以创建有不同操作权限的角色了)

新增用户时,就可以直接选择对应角色,维护好用户和角色的对应关系,用户就有了对应角色的权限了(用户登录后,查询用户对应菜单权限以及按钮权限,前端可以直接根据返回权限展示菜单及按钮权限【此时连菜单权限也一并做了】)。

其实到这里基本就不会有什么问题了,不同角色用户,只能操作自己有的权限操作。

但是以防万一,后端对应接口加个校验会更加安全。

后端权限校验

注解校验

因为后端用的Java的springBoot框架,可以很方便的进行aop操作。使用注解来实现鉴权操作。

注解如下:

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresPermissions
{
    /**
     * 需要校验的权限码
     */
    String[] value() default {};

    /**
     * 验证模式:AND | OR,默认AND
     *(这个属性对应场景如下:
     * 前端有多个按钮操作可能是调用一个接口/
     * 后端新增或者编辑定义的是一个接口)
     * 前者属性用 OR,后者用 AND 【权限会存在 and/or 的关系】
     */
    Logical logical() default Logical.AND;
}

aop切面类

java 复制代码
@Aspect
@Component
public class PreAuthorizeAspect
{
    /**
     * 构建
     */
    public PreAuthorizeAspect()
    {}

    /**
     * 定义AOP签名 (切入所有使用鉴权注解的方法)【切点】
     */
    public static final String POINTCUT_SIGN = " @annotation(com.smart.common.security.annotation.RequiresLogin) || "
            + "@annotation(com.smart.common.security.annotation.RequiresPermissions) || "
            + "@annotation(com.smart.common.security.annotation.RequiresRoles)";

    /**
     * 声明AOP签名
     */
    @Pointcut(POINTCUT_SIGN)
    public void pointcut()
    {}

    /**
     * 环绕切入
     * 
     * @param joinPoint 切面对象
     * @return 底层方法执行后的返回值
     * @throws Throwable 底层方法抛出的异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable
    {
        // 注解鉴权
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        checkMethodAnnotation(signature.getMethod());
        try
        {
            // 执行原有逻辑
            Object obj = joinPoint.proceed();
            return obj;
        }
        catch (Throwable e)
        {
            throw e;
        }
    }

    /**
     * 对一个Method对象进行注解检查
     */
    public void checkMethodAnnotation(Method method) {
        // 校验 @RequiresLogin 注解
        RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class);
        if (requiresLogin != null) {
            AuthUtil.checkLogin();
        }

        // 校验 @RequiresRoles 注解
        RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
        if (requiresRoles != null) {
            AuthUtil.checkRole(requiresRoles);
        }

        // 校验 @RequiresPermissions 注解 【主要看这个校验逻辑】
        RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
        if (requiresPermissions != null) {
            AuthUtil.checkPermi(requiresPermissions);
        }
    }
}

核心处理逻辑在这AuthUtil.checkPermi

java 复制代码
    public void checkPermi(RequiresPermissions requiresPermissions) {
	SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ","));
		// 判断是否有所有权限或者是单个权限就可以
        if (requiresPermissions.logical() == Logical.AND) {
            checkPermiAnd(requiresPermissions.value());
        }
        else {
            checkPermiOr(requiresPermissions.value());
        }
    }

checkPermiAnd 权限与逻辑,所有权限满足才能调用该方法

java 复制代码
    public void checkPermiAnd(String... permissions) {
        Set<String> permissionList = getPermiList();
        for (String permission : permissions) {
            if (!hasPermi(permissionList, permission)) {
                throw new NotPermissionException(permission);
            }
        }
    }

hasPermi逐个判断是否有对应操作权限,只要一个不满足直接抛异常。

checkPermiOr 权限或逻辑,只要有权限满足就能直接调用该方法

java 复制代码
    public void checkPermiOr(String... permissions) {
        // 登录后将用户信息存在ThreadLocal中,直接获取登录用户操作权限列表
        Set<String> permissionList = getPermiList();
        for (String permission : permissions) {
            if (hasPermi(permissionList, permission)) {
                return;
            }
        }
        if (permissions.length > 0) {
            throw new NotPermissionException(permissions);
        }
    }
java 复制代码
    // 所有权限标识
	private static final String ALL_PERMISSION = "*:*:*";

	// 【or 逻辑】走到这里,只要一个满足条件直接结束,鉴权结束,不会抛异常
	public boolean hasPermi(Collection<String> authorities, String permission) {
        // 前半段校验是否有所有权限,后半段匹配当前权限是否在当前用户权限中
        return authorities.stream().filter(StringUtils::hasText)
                .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
    }

接口使用的话就比较简单了,直接加个注解添加最开始前端传给后端保存的操作标识即可,如下:

这下前后端双保险,按钮级操作权限到此已经实现。撒花✿✿ヽ(°▽°)ノ✿

相关推荐
慧都小妮子9 分钟前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m512713 分钟前
LinuxC语言
java·服务器·前端
IU宝18 分钟前
C/C++内存管理
java·c语言·c++
瓜牛_gn19 分钟前
依赖注入注解
java·后端·spring
hakesashou20 分钟前
Python中常用的函数介绍
java·网络·python
佚先森29 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
古月居GYH42 分钟前
在C++上实现反射用法
java·开发语言·c++
儿时可乖了1 小时前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite