前后端分离项目接口权限检查方案
基于handleMethod写的一款分级式接口权限检查方案。
权限自动同步机制(启动更新,页面不提供增删改):
public class AuthorizationMappingGenerateExecutor implements EasyApplicationRunner {@Autowiredprivate AuthorizationMappingMapper authorizationMappingMapper;@Autowiredprivate RequestMappingHandlerMapping mapping;@Autowiredprivate SupportSecurityProperties securityProperties;// 启动时执行一次,随便用用作个排序。没安全问题int i = 0;@Overridepublic void doBusiness() {Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = mapping.getHandlerMethods();Collection<HandlerMethod> handlerMethods = handlerMethodMap.values();// 表中已存在的codeList<String> existsCodes = authorizationMappingMapper.selectProperties(Wrappers.lambdaQuery(), AuthorizationMapping::getCode);// 排重setSet<String> currentExistsCodes = new HashSet<>();// 配置的级别MappingLevel mappingLevel = securityProperties.getMappingLevel();// 添加的权限List<AuthorizationMapping> insertMappings = new ArrayList<>();// 需要更新的权限List<AuthorizationMapping> updateMappings = new ArrayList<>();for (HandlerMethod handlerMethod : handlerMethods) {Class<?> beanType = handlerMethod.getBeanType();String name = beanType.getName();// 只取本系统内的接口if (!name.contains("xxxx")) {continue;}AuthorizationMapping moduleMapping = buildModuleMapping(handlerMethod);int type = chooseType(existsCodes, currentExistsCodes, moduleMapping);if (type == 1) {insertMappings.add(moduleMapping);} else if (type == 2) {updateMappings.add(moduleMapping);}// 只开启了一级权限检查if (mappingLevel.is(MappingLevel.MODULE)) {continue;}AuthorizationMapping controllerMapping = buildControllerMapping(moduleMapping.getCode(), handlerMethod);int controllerType = chooseType(existsCodes, currentExistsCodes, controllerMapping);if (controllerType == 1) {insertMappings.add(controllerMapping);} else if (controllerType == 2) {updateMappings.add(controllerMapping);}// 只开启了二级权限检查if (mappingLevel.is(MappingLevel.CONTROLLER)) {continue;}// 方法级的权限检查AuthorizationMapping methodMapping = buildMethodMapping(controllerMapping.getCode(), handlerMethod);int methodType = chooseType(existsCodes, currentExistsCodes, methodMapping);if (methodType == 1) {insertMappings.add(methodMapping);} else if (methodType == 2) {updateMappings.add(methodMapping);}}// 批量插入authorizationMappingMapper.insertSplitBatch(insertMappings, 200);// 配置是否更新,一条条更新太慢,影响启动速度。if (securityProperties.getEnableMappingUpdate()) {updateMappings.forEach(authorizationMappingMapper::updateById);}// 清掉子级,不占表空间。开放的话重启构建即可。code不会变LambdaQueryWrapper<AuthorizationMapping> wrapper = Wrappers.lambdaQuery();if (mappingLevel.is(MappingLevel.MODULE)) {wrapper.in(AuthorizationMapping::getLevel, ZYListUtils.toList(MappingLevel.CONTROLLER.level(), MappingLevel.METHOD.level()));} else if (mappingLevel.is(MappingLevel.CONTROLLER)) {wrapper.eq(AuthorizationMapping::getLevel, MappingLevel.METHOD.level());}authorizationMappingMapper.delete(wrapper);}private int chooseType(List<String> existsCodes, Set<String> currentExistsCodes, AuthorizationMapping methodMapping) {String code = methodMapping.getCode();if (currentExistsCodes.contains(code)) {return 0;}// 去重currentExistsCodes.add(methodMapping.getCode());// 更新or插入if (!existsCodes.contains(code)) {return 1;} else {return 2;}}// 构建模块权限private AuthorizationMapping buildModuleMapping(HandlerMethod handlerMethod) {Class<?> beanType = handlerMethod.getBeanType();Package aPackage = beanType.getPackage();AuthorizationMapping packageMapping = buildBaseMapping(MappingLevel.MODULE);packageMapping.setMapping("");packageMapping.setParentCode(ZYTreeUtils.TREE_ROOT_ID);String moduleCode = PermissionMappingGenerator.generateModuleKey(handlerMethod);packageMapping.setCode(moduleCode);String packageName = aPackage.getName();packageMapping.setFileLocal(packageName);String packageChinaName = try2getPackageChinaName(handlerMethod);packageMapping.setName(null != packageChinaName ? packageChinaName : packageName);return packageMapping;}// 构建controller级权限private AuthorizationMapping buildControllerMapping(String parentCode, HandlerMethod handlerMethod) {AuthorizationMapping packageMapping = buildBaseMapping(MappingLevel.CONTROLLER);packageMapping.setMapping("");packageMapping.setParentCode(parentCode);String controllerCode = PermissionMappingGenerator.generateControllerKey(handlerMethod);packageMapping.setCode(controllerCode);Class<?> beanType = handlerMethod.getBeanType();packageMapping.setFileLocal(beanType.getName());Theme theme = beanType.getAnnotation(Theme.class);packageMapping.setName(null != theme ? theme.value() : beanType.getSimpleName());return packageMapping;}// 构建方法级权限private AuthorizationMapping buildMethodMapping(String parentCode, HandlerMethod handlerMethod) {String methodCode = PermissionMappingGenerator.generateMethodKey(handlerMethod);AuthorizationMapping packageMapping = buildBaseMapping(MappingLevel.METHOD);packageMapping.setCode(methodCode);packageMapping.setParentCode(parentCode);Class<?> beanType = handlerMethod.getBeanType();Method method = handlerMethod.getMethod();packageMapping.setFileLocal(beanType.getName() + "." + method.getName());packageMapping.setMapping(spellMapping(method, beanType));packageMapping.setName(getMappingName(handlerMethod));return packageMapping;}private AuthorizationMapping buildBaseMapping(MappingLevel level) {AuthorizationMapping packageMapping = new AuthorizationMapping();packageMapping.setCreateDate(System.currentTimeMillis());packageMapping.setIsMiniAuth(0);packageMapping.setSort(++i);packageMapping.setLevel(level.level());packageMapping.setRemarks(level.getMappingName());return packageMapping;}private String getMappingName(HandlerMethod handlerMethod) {SystemLog systemLog = handlerMethod.getMethodAnnotation(SystemLog.class);if (null != systemLog) {return systemLog.value();}return handlerMethod.getMethod().getName();}private String spellMapping(Method method, Class<?> beanType) {StringBuilder path = new StringBuilder();RequestMapping parentRequestMapping = beanType.getAnnotation(RequestMapping.class);if (null != parentRequestMapping) {String[] value = parentRequestMapping.value();if (value.length > 0) {path.append(value[0]);}}RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);GetMapping methodGetRequestMapping = method.getAnnotation(GetMapping.class);PostMapping methodPostRequestMapping = method.getAnnotation(PostMapping.class);if (null != methodRequestMapping) {String[] value = methodRequestMapping.value();if (value.length > 0) {path.append(value[0]);}} else if (null != methodGetRequestMapping) {String[] value = methodGetRequestMapping.value();if (value.length > 0) {path.append(value[0]);}} else if (null != methodPostRequestMapping) {String[] value = methodPostRequestMapping.value();if (value.length > 0) {path.append(value[0]);}}return path.toString();}private String try2getPackageChinaName(HandlerMethod handlerMethod) {Class<?> beanType = handlerMethod.getBeanType();Package aPackage = beanType.getPackage();Theme packageTheme = aPackage.getAnnotation(Theme.class);if (null != packageTheme) {return packageTheme.value();}return null;}
}
权限检查拦截器
public class AuthorizationMappingInterceptor implements EasyMvcInterceptor, InitializingBean {private final static Set<String> NO_NEED_CHECK_CODES = new HashSet<>();private final static Set<String> CHECK_CODES = new HashSet<>();@Autowiredprivate AuthorizationMappingMapper authorizationMappingMapper;@Autowiredprivate SupportSecurityProperties securityProperties;@Overridepublic boolean doBusiness(HandlerMethod handlerMethod, HttpServletRequest request, HttpServletResponse response) {// 开放接口if (ZYRequestUtils.isDirectApi()) {return true;}// 超管if (UserHelper.isSuperAdmin()) {return true;}// 需要跳过的权限if (isSkipMappingCheck(handlerMethod)) {return true;}// 查询需要的权限MappingLevel mappingLevel = securityProperties.getMappingLevel();String permissionCode = PermissionMappingGenerator.generateByLevel(handlerMethod, mappingLevel);// 缓存在存在,直接通过检查if (NO_NEED_CHECK_CODES.contains(permissionCode)) {return true;}// 该接口没有做权限限制,直接放行if (!CHECK_CODES.contains(permissionCode)) {NO_NEED_CHECK_CODES.add(permissionCode);return true;}// 用户权限信息LoginUser loginAreaUser = UserHelper.getLoginAreaUser();Set<String> mappingCodes = loginAreaUser.getMappingCodes();if (!hasPermission(permissionCode, mappingCodes)) {throw new PermissionException("您没有访问接口的权限");}return true;}private boolean hasPermission(String permissionCode, Set<String> mappingCodes) {return null != mappingCodes && mappingCodes.contains(permissionCode);}@Overridepublic void afterPropertiesSet() {// 根据权限级别查询需要检查的HandleMethod或controllerMappingLevel mappingLevel = securityProperties.getMappingLevel();LambdaQueryWrapper<AuthorizationMapping> wrapper = Wrappers.lambdaQuery();wrapper.eq(AuthorizationMapping::getLevel, mappingLevel.level());wrapper.eq(AuthorizationMapping::getIsMiniAuth, NO);wrapper.select(AuthorizationMapping::getCode);List<String> authorizationMappings = authorizationMappingMapper.selectProperties(wrapper, AuthorizationMapping::getCode);CHECK_CODES.addAll(authorizationMappings);}private boolean isSkipMappingCheck(HandlerMethod handlerMethod) {Class<?> beanType = handlerMethod.getBeanType();Package aPackage = beanType.getPackage();SkipMappingCheck packageSkip = aPackage.getAnnotation(SkipMappingCheck.class);if (null != packageSkip) {return true;}SkipMappingCheck classSkip = beanType.getAnnotation(SkipMappingCheck.class);if (null != classSkip) {return true;}Method method = handlerMethod.getMethod();SkipMappingCheck methodSkip = method.getAnnotation(SkipMappingCheck.class);return null != methodSkip;}
}
public enum MappingLevel implements Matcher {MODULE("模块"),CONTROLLER("控制类"),METHOD("接口方法");private String mappingName;public String level() {return this.name().toLowerCase();}@Overridepublic Object statusCode() {return level();}
}
code生成器
public class PermissionMappingGenerator {private final static Map<String, String> PACKAGE_KEY_CACHE = new HashMap<>();private final static Map<String, String> CONTROLLER_KEY_CACHE = new HashMap<>();private final static Map<String, String> METHOD_KEY_CACHE = new HashMap<>();public static String generateByLevel(HandlerMethod handlerMethod, MappingLevel mappingLevel) {switch (mappingLevel) {case METHOD:return generateMethodKey(handlerMethod);case CONTROLLER:return generateControllerKey(handlerMethod);case MODULE:return generateModuleKey(handlerMethod);default:throw new LocalException("不能确定的权限级别");}}public static String generateModuleKey(HandlerMethod handlerMethod) {Class<?> beanType = handlerMethod.getBeanType();Package aPackage = beanType.getPackage();String packageName = aPackage.getName();String cryptPackageCode = PACKAGE_KEY_CACHE.get(packageName);if (ZYStrUtils.isNull(cryptPackageCode)) {cryptPackageCode = ZYCryptUtils.encryptMD5(packageName).toLowerCase();PACKAGE_KEY_CACHE.put(packageName, cryptPackageCode);}return cryptPackageCode;}public static String generateControllerKey(HandlerMethod handlerMethod) {Class<?> beanType = handlerMethod.getBeanType();String className = beanType.getName();String cryptControllerCode = CONTROLLER_KEY_CACHE.get(className);if (ZYStrUtils.isNull(cryptControllerCode)) {cryptControllerCode = ZYCryptUtils.encryptMD5(className).toLowerCase();CONTROLLER_KEY_CACHE.put(className, cryptControllerCode);}return cryptControllerCode;}public static String generateMethodKey(HandlerMethod handlerMethod) {Class<?> beanType = handlerMethod.getBeanType();Method method = handlerMethod.getMethod();List<String> keySign = new ArrayList<>();keySign.add(beanType.getSimpleName());keySign.add(method.getName());Parameter[] parameters = method.getParameters();for (Parameter parameter : parameters) {keySign.add(parameter.getName());}String key = StrUtils.join(keySign, "#");String cryptKey = METHOD_KEY_CACHE.get(key);if (null == cryptKey) {cryptKey = CryptUtils.encryptMD5(key).toLowerCase();METHOD_KEY_CACHE.put(key, cryptKey);}return cryptKey;}
}
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PACKAGE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipMappingCheck {
}
与传统的路径匹配接口权限检查相比,本方案采用HandleMethod信息与角角直接绑定。多了一个权限颗粒度划分。对减少接品权限数量及减少人工分配量有明显的优势。自动的权限生成工具省去了维护权限编号协删改查的人工操作。减去了大量人工。实际使用中,默认使用module级,维护量很少。较严情况下,使用controller级。严格安全等保要求的情况下。使用mothod级。方案选择很灵活。重启服务即可任意切换。可以使用SkipMappingCheck 注解对整个模块,整个controller进行跳过处理。
相关文章:
前后端分离项目接口权限检查方案
基于handleMethod写的一款分级式接口权限检查方案。 权限自动同步机制(启动更新,页面不提供增删改): public class AuthorizationMappingGenerateExecutor implements EasyApplicationRunner {Autowiredprivate AuthorizationMap…...
步入React正殿 - 事件处理
目录 扩展学习资料 React事件和DOM事件 和传统DOM事件处理异同 this关键字的处理 this关键字 在JSX中使用bind方法 在构造函数中使用bind方法 使用箭头函数【推荐】 向事件处理程序传递参数【不跨组件】 向父组件传递参数 /src/App.js /src/components/listItem.jsx…...
NLP(六十四)使用FastChat计算LLaMA-2模型的token长度
LLaMA-2模型部署 在文章NLP(五十九)使用FastChat部署百川大模型中,笔者介绍了FastChat框架,以及如何使用FastChat来部署百川模型。 本文将会部署LLaMA-2 70B模型,使得其兼容OpenAI的调用风格。部署的Dockerfile文件…...
个保新标 | 《信息安全技术 敏感个人信息处理安全要求》(征求意见稿)发布
8 月 9 日,全国信息安全标准化技术委员会公开发布关于国家标准《信息安全技术 敏感个人信息处理安全要求》(征求意见稿)(以下简称《标准》)的通知,面向社会广泛征求意见。 《标准》的制定背景是为支撑《个人…...
【uniapp 返回顶部】
返回顶部 参数说明示例官方链接 uni.pageScrollTo(OBJECT) 将页面滚动到目标位置。 参数说明 参数名类型必填说明scrollTopNumber否滚动到页面的目标位置(单位px)selectorString否选择器,App、H5、微信小程序2.7.3 、支付宝小程序1.20.0支持…...
无代码集成励销云CRM连接更多应用
场景描述: 基于励销云的开放API,实现无代码集成连接励销云与其它应用。通过Aboter可轻松搭建业务自动化流程,实现多个应用之间的数据连接。 接口能力: 用户模块业务模块拜访签到模块公海客户模块联系人模块合同模块客户模块任务…...
QT自带PDF库的使用
QT自带PDF库可以方便的打开PDF文件,并将文件解析为QImage,相比网上提供的开源库,QT自带PDF库使用更方便,也更加可靠,然而,QT自带PDF库的使用却不同于其他通用库的使用,具备一定的技巧。 1. 安装…...
SQL | 排序检索的数据
3-排序检索的数据 使用order by语句排序检索到的数据。 3.1-排序数据 使用SQL语句返回一个数据表的列。 select prod_id from products; --------------------- | prod_name | --------------------- | 8 inch teddy bear | | 12 inch teddy bear | | 18 inch teddy bear |…...
8. yaml文件管理
文章目录 yaml文件管理编写yaml配置文件获取配置模板方法一方法二方法三方法四 yaml文件管理 Kubernetes 支持 YAML 和 JSON 格式管理资源对象 JSON 格式:主要用于 api 接口之间消息的传递YAML 格式:用于配置和管理,YAML 是一种简洁的非标记性…...
Cobbler自定义yum源
再次了解下Cobbler的目录结构: 在/var/www/cobbler/ks_mirror目录下存放的是所有的镜像。 存放的是仓库镜像: 在/var/lib/cobbler/kickstarts目录下是存放的所有的kickstarts文件。 再有就是/etc/cobbler这个目录: [rootvm1 loaders]# cd /…...
《算法竞赛·快冲300题》每日一题:“特殊数字”
《算法竞赛快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 特…...
在R中比较两个矩阵是否相等
目录 方法一:使用all.equal()比较两个R对象是否近似相等 方法二:使用identical比较两个R对象是否精确相等。 方法一:使用all.equal()比较两个R对象是否近似相等 使用函数:all.equal(x,y) 比较两个R对象x和y是否近似相等 > M1…...
商城-学习整理-基础-商品服务API-属性分组(七)
目录 一、创建系统菜单二、开发商品系统-平台属性-属性分组1、将三级分类功能抽取出来2、编写后端代码3、属性分组新增功能4、属性分组修改回显功能 三、商品系统-平台属性-规则参数1、列表展示页面2、新增规格参数页面 四、商品系统-平台属性-销售属性1、列表展示页面2、新增或…...
什么是响应式设计?列举几种实现响应式设计的方法。
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是响应式设计?⭐ 实现响应式设计的方法⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏…...
Java类和对象(一文读懂)
文章目录 类、对象是什么?创建类构造器 创建对象 类、对象是什么? 类:类是一个模板,它描述一类对象的行为和状态。类可以看成是创建 Java 对象的模板。 对象:对象是类的一个实例(对象不是找个女朋友&#x…...
用友移动管理系统 任意文件上传漏洞复现(HW0day)
0x01 产品简介 用友移动系统管理是用友公司推出的一款移动办公解决方案,旨在帮助企业实现移动办公、提高管理效率和员工工作灵活性。它提供了一系列功能和工具,方便用户在移动设备上管理和处理企业的系统和业务。 0x02 漏洞概述 用友移动管理系统 uploa…...
启动springboot,出现Unable to start embedded Tomcat
报错信息 org.apache.catalina.core.ContainerBase : A child container failed during startjava.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbedd…...
加密和安全
加密和安全 一.安全机制 安全攻击的几种典型方式: STRIDE Spoofing 假冒 Tampering 篡改 Repudiation 否认 Information Disclosure 信息泄漏 Denial of Service 拒绝服务 Elevation of Privilege 提升…...
Maven基础总结
前言 Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。 基本要求掌握 配置Maven环境直接查。 得会在IDEA创建Maven的java项目吧、会创建Maven的web项目吧、会创建多模块项目吧。 得会配置插件pligin、依赖dependency吧 一、Maven四大特性 1、…...
Java 编程实战:如何用 Java 编写一个简单而强大的 Tomcat
学习完了JavaWeb,为了深入了解tomcat,打算手撕tomcat搭建自己的tomcat,希望对来访小伙伴也有帮助 引言 Tomcat 是一个开源的 Web 服务器和 Servlet 容器,它可以提供动态 Web 内容的处理和交互功能。Tomcat 是用 Java 语言编写的&a…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
