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

背景

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

角色配置前后端操作

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

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

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

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

[{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文件,渲染出的角色权限编辑页面如下:

image-20231031125412416

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

{"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表中,表结构如下:

image-20231031130430532

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

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

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

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

后端权限校验

注解校验

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

注解如下:

@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切面类

@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

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

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

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

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

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

    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);}}
    // 所有权限标识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));}

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

image-20231031154546860

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/176454.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业 Tomcat 运维 部署tomcat反向代理集群

一、Tomcat 简介 Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c; Tomcat和Nginx、Apache(httpd)、Web服务器一样&#xff0c;具有处理HTML页面的功能不过Tomcat处理静态HTML的能力不如Nginx/Apache服务器 一个tomcat默认并…

Docker之docker-compose(介绍,安装及入门示例)

文章目录 一、docker-compose介绍Compose 中有两个重要的概念&#xff1a; 二、docker-compose安装三、docker-compose简单示例参考网址&#xff1a; 一、docker-compose介绍 Compose 项目是 Docker 官方的开源项目&#xff0c;负责实现对 Docker 容器集群的快速编排。 Compo…

前度开发面试题

面试题总结 vue页面跳转会经过两个钩子函数beforeEach、afterEach 组见守卫 beforeRouteEnter前置组见守卫 *beforeRouteUpdate更新之前 watch和computed区别 数据没有改变&#xff0c;则 computed 不会重新计算&#xff09;。若没改变&#xff0c;计算属性会立即返回之前缓…

网际协议IP

网际协议IP 一、IP地址 1、分类的IP地址 IP地址::{<网络号>,<主机号>} 2、无分类编址CIDR IP地址::{<网络前缀>,<主机号>} &#xff08;1&#xff09;网络前缀 ​ 与分类IP最大的区别就是网络前缀的位数n是不固定的&#xff0c;可以是0~32位。 ​ …

月入8K, 计算机专业应届女孩转行5G网络优化工程师,她说:这行请慎入

小C说&#xff0c;如果要用两个字描述23年计算机专业的就业心情&#xff0c;那就是“焦虑”&#xff1b;用三个字描述23年计算机专业的就业环境&#xff0c;那就是“卷麻了”。 得益于张雪峰老师的就业推荐计算机专业需求的日益减少&#xff0c;2023年&#xff0c;计算机专业成…

三、 链表

一、链表的定义 链表是一种动态数据结果&#xff0c;内存分配不是在创建链表时一次性完成的&#xff0c;每添加一个节点&#xff0c;分配一次内存&#xff0c;由于没有闲置的内存&#xff0c;链表的空间效率高于数组 二、定义单向链表 struct ListNode {int m_nValue;ListNo…

DevChat:VSCode中基于大模型的AI智能编程助手

#AI编程助手哪家好&#xff1f;DevChat“真”好用# 文章目录 1. 前言2. 安装2.1 注册新用户2.2 在VSCode中安装DevChat插件2.3 设置Access Key 3. 实战使用4. 总结 1. 前言 DevChat是由Merico公司精心打造的AI智能编程助手。它利用了最先进的大语言模型技术&#xff0c;像人类…

nodejs+vue智慧补助系统的设计与实现-计算机毕业设计

随着网络技术的不断发展&#xff0c;多媒体技术应用渐渐的出现在教育领域中&#xff0c;智慧补助系统已经成为教育发展的一个热门话题。 在众多网络开发技术中&#xff0c;nodejs是当前很热门的一种软件&#xff0c;因为它可以进行数据库操作及方便用户控制管理。 在各学校的教…

QML 创建 Web 混合应用

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 随着互联网的快速发展,Web 应用在各个领域中变得越来越流行。为了满足用户对多样化功能的需求,我们经常需要将 Web 技术和原生应用相结合,来创建混合应用程序。 混合应用程序:是一种应用程序开发方法,它…

k8s、pod

Pod k8s中的port【端口&#xff1a;30000-32767】 port &#xff1a;为Service 在 cluster IP 上暴露的端口 targetPort&#xff1a;对应容器映射在 pod 端口上 nodePort&#xff1a;可以通过k8s 集群外部使用 node IP node port 访问Service containerPort&#xff1a;容…

错误: 找不到或无法加载主类 回归java运行的本质

错误: 找不到或无法加载主类 回归java运行的本质 一&#xff0c;背景 当有了idea这种工具后&#xff0c;java的mian方法执行起来是如此简单&#xff0c;很少有人再手动编辑并通过命令行执行了。 同时&#xff0c;在当今Spring Boot盛行的今天&#xff0c;恐怕很少再有人执行j…

跟着步骤,快速实现图书行业小程序商城

跟着步骤&#xff0c;快速实现图书行业小程序商城 打造独特图书购物体验&#xff0c;小程序商城制作指南 轻松搭建图书馆与书店的线上商城小程序 值得一试的图书教材小程序商城搭建方法 图书商城小程序制作指南&#xff0c;助你成为行业领袖 实战教程&#xff1a;如何制作…

Android开发知识学习——HTTPS

文章目录 定义HTTPS连接HTTPS 连接建立的过程课后题 定义 HTTP Secure / HTTP over SSL / HTTP over TLS SSL&#xff1a;Secure Socket Layer -> TLS Transport Layer Security 定义&#xff1a;在HTTP之下增加的一个安全层&#xff0c;用于保障HTTP的加密传输 本质&…

云计算助力史上首届“云上亚运”圆满成功!

201金&#xff0c;魔幻的BGM&#xff0c;以及崛起的中国科技&#xff0c;让杭州亚运会成功出圈。 很多网友表示太震撼了&#xff01;开幕式很漂亮&#xff0c;杭州为了奥运造新城真豪横&#xff0c;看完一整个文化自信住&#xff01; 赛场内外除了无数个令人感动的瞬间&#…

FPGA时序分析与约束(8)——时序引擎

一、概述 要想进行时序分析和约束&#xff0c;我们需要理解时序引擎究竟是如何进行时序分析的&#xff0c;包括时序引擎如何进行建立分析&#xff08;setup&#xff09;&#xff0c;保持分析(hold)&#xff0c;恢复时间分析(recovery)和移除时间分析(removal)。 二、时序引擎进…

Django添加csrf保护机制

步骤 要在Django中启用CSRF保护&#xff0c;您可以按照以下步骤进行操作&#xff1a; 1. 在Django的settings.py文件中&#xff0c;确保django.middleware.csrf.CsrfViewMiddleware中间件已添加到MIDDLEWARE设置中。通常&#xff0c;这个中间件默认就会包含在其中。 2. 在HTM…

一文速通 StarRocks 数据库:核心概念、架构与特性

Author: Xinyao Tian 概述 本文档简要梳理了 StarRocks 的基本信息。 简介 Introduction StarRocks 是面向下个时代的&#xff0c;高性能的数据分析仓库。其提供了实时、多维度、高并发的数据分析能力。 StarRocks is a next-gen, high-performance analytical data warehou…

杂牌行车记录仪特殊AVI结构恢复案例

最近遇到一个杂牌的行车记录仪需要恢复数据&#xff0c;其使用AVI格式&#xff0c;但是在扫描恢复的过程中却发现厂家对其AVI结构进行了“魔改”致程序无法正常识别 故障存储:16G SD卡 fat32文件系统 故障现象: 16G的SD卡&#xff0c;在发生事故后客户尝试自行接到手机上读…

在 Visual Studio 中远程调试 C++ 项目

目录 一、说明二、下载远程工具1. 官网下载2. 自己电脑上拷贝 三、 运行远程工具四、本机Visual Studio配置五、自动部署 一、说明 参考官方文档&#xff1a;https://learn.microsoft.com/zh-cn/visualstudio/debugger/remote-debugging-cpp?viewvs-2022 二、下载远程工具 …

解决:http://localhost:8080 不在以下 request 合法域名列表中

在搭建资源服务器时&#xff0c;遇到了微信开发者工具中无法访问本地资源服务器的情况&#xff0c;报错如下&#xff1a; 参考一篇博文的方法&#xff0c;完美解决 【解决】http://localhost:8080 不在以下 request 合法域名列表中_localhost不在以下 request 合法域名列表中-…