Java项目接口权限校验的灵活实现

引言

在Java Web开发中,接口权限校验是保护系统资源安全的关键机制。本文将介绍一种灵活、可配置的接口权限校验方案,通过注解驱动和拦截器实现,既能保证安全性,又能灵活控制哪些接口需要校验。

设计思路

实现方案的核心设计要点:

  1. 注解驱动:使用自定义注解标记需要权限校验的接口
  2. 拦截器机制:在请求处理前进行统一权限校验
  3. 灵活配置:支持方法级和类级配置,可动态调整
  4. 权限缓存:提高权限验证效率

在这里插入图片描述

实现步骤

1. 定义权限校验注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionCheck {/*** 权限标识,支持多个权限(满足任意一个即可)*/String[] value() default {};/*** 是否开启权限校验(默认开启)*/boolean enabled() default true;/*** 逻辑关系:AND-需满足所有权限,OR-满足任意权限*/Logical logical() default Logical.OR;
}public enum Logical {AND, OR
}

2. 实现权限校验拦截器

@Component
public class PermissionInterceptor implements HandlerInterceptor {@Autowiredprivate PermissionService permissionService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 如果不是Controller方法直接放行if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();// 获取方法上的注解PermissionCheck methodAnnotation = method.getAnnotation(PermissionCheck.class);// 获取类上的注解PermissionCheck classAnnotation = method.getDeclaringClass().getAnnotation(PermissionCheck.class);// 1. 如果方法上明确关闭权限校验if (methodAnnotation != null && !methodAnnotation.enabled()) {return true;}// 2. 如果类上关闭权限校验且方法未指定if (classAnnotation != null && !classAnnotation.enabled() && (methodAnnotation == null || methodAnnotation.enabled())) {return true;}// 3. 获取当前用户权限User currentUser = getCurrentUser(request);if (currentUser == null) {response.sendError(HttpStatus.UNAUTHORIZED.value(), "用户未登录");return false;}// 4. 获取需要的权限Set<String> requiredPermissions = getRequiredPermissions(methodAnnotation, classAnnotation);// 5. 无需权限校验if (requiredPermissions.isEmpty()) {return true;}// 6. 检查权限boolean hasPermission = checkPermissions(currentUser, requiredPermissions, methodAnnotation != null ? methodAnnotation.logical() : (classAnnotation != null ? classAnnotation.logical() : Logical.OR));if (!hasPermission) {response.sendError(HttpStatus.FORBIDDEN.value(), "权限不足");return false;}return true;}private Set<String> getRequiredPermissions(PermissionCheck methodAnnotation, PermissionCheck classAnnotation) {Set<String> permissions = new HashSet<>();// 方法注解优先if (methodAnnotation != null && methodAnnotation.value().length > 0) {Collections.addAll(permissions, methodAnnotation.value());return permissions;}// 类注解if (classAnnotation != null && classAnnotation.value().length > 0) {Collections.addAll(permissions, classAnnotation.value());}return permissions;}private boolean checkPermissions(User user, Set<String> requiredPermissions, Logical logical) {// 获取用户实际权限(可从缓存中获取)Set<String> userPermissions = permissionService.getUserPermissions(user.getId());if (logical == Logical.AND) {return userPermissions.containsAll(requiredPermissions);} else {return requiredPermissions.stream().anyMatch(userPermissions::contains);}}
}

3. 注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate PermissionInterceptor permissionInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(permissionInterceptor).addPathPatterns("/api/**")  // 拦截API路径.excludePathPatterns("/api/public/**"); // 排除公共接口}
}

4. 权限服务实现

@Service
public class PermissionServiceImpl implements PermissionService {@Autowiredprivate PermissionMapper permissionMapper;@Autowiredprivate CacheManager cacheManager;@Overridepublic Set<String> getUserPermissions(Long userId) {// 从缓存获取Cache cache = cacheManager.getCache("userPermissions");Cache.ValueWrapper wrapper = cache.get(userId);if (wrapper != null) {return (Set<String>) wrapper.get();}// 从数据库查询Set<String> permissions = permissionMapper.selectPermissionsByUserId(userId);// 放入缓存cache.put(userId, permissions);return permissions;}
}

使用示例

1. 类级别权限控制

@RestController
@RequestMapping("/users")
@PermissionCheck(value = {"USER_MANAGE"}, logical = Logical.AND)
public class UserController {// 需要USER_MANAGE权限@GetMappingpublic List<User> listUsers() {// ...}// 不需要权限校验(覆盖类级别设置)@PermissionCheck(enabled = false)@GetMapping("/public")public User getPublicUser() {// ...}
}

2. 方法级别权限控制

@RestController
@RequestMapping("/products")
public class ProductController {// 需要PRODUCT_READ权限@PermissionCheck("PRODUCT_READ")@GetMappingpublic List<Product> listProducts() {// ...}// 需要同时具备PRODUCT_WRITE和PRODUCT_MANAGE权限@PermissionCheck(value = {"PRODUCT_WRITE", "PRODUCT_MANAGE"}, logical = Logical.AND)@PostMappingpublic Product createProduct(@RequestBody Product product) {// ...}
}

3. 公共接口(无需权限)

@RestController
@RequestMapping("/public")
public class PublicController {// 无需任何权限校验@GetMapping("/info")public SystemInfo getSystemInfo() {// ...}
}

进阶优化

1. 动态权限配置

可将权限配置存储在数据库中,实现动态管理:

CREATE TABLE api_permission (id BIGINT PRIMARY KEY AUTO_INCREMENT,api_path VARCHAR(255) NOT NULL,http_method VARCHAR(10) NOT NULL,permission_code VARCHAR(50) NOT NULL,enabled BOOLEAN DEFAULT true,UNIQUE KEY uni_api_method (api_path, http_method)
);

在拦截器中增加数据库权限检查逻辑:

// 在PermissionInterceptor中增加
private boolean checkDynamicPermission(String path, String method) {List<String> requiredPermissions = permissionService.getPermissionsForApi(path, method);if (requiredPermissions.isEmpty()) {return true; // 无配置表示不需要权限}// 检查用户权限...
}

2. 权限缓存策略

使用Redis缓存用户权限数据,提高性能:

@Configuration
public class RedisConfig {@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30))  // 30分钟过期.disableCachingNullValues();return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}@Service
public class RedisPermissionService implements PermissionService {@Autowiredprivate RedisTemplate<String, Set<String>> redisTemplate;@Overridepublic Set<String> getUserPermissions(Long userId) {String key = "user:permissions:" + userId;Set<String> permissions = redisTemplate.opsForSet().members(key);if (permissions == null || permissions.isEmpty()) {// 从数据库加载permissions = permissionMapper.selectPermissionsByUserId(userId);if (!permissions.isEmpty()) {redisTemplate.opsForSet().add(key, permissions.toArray(new String[0]));redisTemplate.expire(key, 30, TimeUnit.MINUTES);}}return permissions;}
}

方案对比

实现方式优点缺点适用场景
拦截器+注解灵活、非侵入、配置简单无法动态修改中小型项目
动态数据库配置可动态调整、集中管理增加数据库访问、实现复杂大型复杂系统
AOP实现解耦彻底、可复用性高配置复杂、学习曲线陡峭需要高度解耦的系统

总结

本文介绍了一种基于注解和拦截器的接口权限校验方案,具有以下特点:

  1. 灵活配置:通过注解控制每个接口的权限要求
  2. 非侵入式:不影响业务逻辑代码
  3. 层次分明:支持类级别和方法级别的权限控制
  4. 易于扩展:可结合数据库实现动态权限管理
  5. 性能优化:通过缓存减少权限查询开销

通过合理的权限校验实现,可以大大提高系统的安全性,同时保持代码的整洁和可维护性。根据项目需求,可以选择合适的实现方式和优化策略。

提示:在实际项目中,建议结合Spring Security或Shiro等安全框架,可以更全面地解决认证授权问题。本文方案适用于需要轻量级权限控制的场景。

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

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

相关文章

瀚高DB兼容MySQL if函数

文章目录环境症状问题原因解决方案环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 症状 MySQL if函数在瀚高DB当中没有&#xff0c;源应用在用到if函数时&#xff0c;就会报if函数不存在的错误信息。为此&#xff0c;我们需要根据业…

基于深度学习的胸部 X 光图像肺炎分类系统(六)

目录 结果指标解读 一、为什么选择这些指标&#xff1f; 二、各指标的定义和解读 1. 准确率&#xff08;Accuracy&#xff09; 2. 损失&#xff08;Loss&#xff09; 3. 精确率&#xff08;Precision&#xff09; 4. 召回率&#xff08;Recall&#xff09; 三、这些指标…

区块链性能优化策略:从理论到实践

目录 区块链性能优化策略:从理论到实践 1. 引言:区块链性能的挑战 2. 性能评估指标 2.1 核心性能指标 2.2 性能瓶颈分析 3. 分层优化策略 3.1 网络层优化 3.1.1 Gossip协议改进 3.1.2 网络分片 3.2 共识层优化 3.2.1 PBFT优化 3.3 数据层优化 3.3.1 状态树优化 3.3.2 区块数据…

【VLLM】open-webui部署模型全流程

目录 前言 一、租用服务器到服务器连接VScode全流程(可选) 二、下载模型到本地服务器 2.1 进入魔塔社区官网 2.2 选择下载模型 2.3 执行下载 三、部署VLLM 3.1 参考vllm官网文档 3.2 查看硬件要求 3.3 安装vLLM框架 3.4 启动模型服务 方法1:直接启动下载的本地模…

办公自动化入门:如何高效将图片整合为PDF文档

将多张图片合并到一个PDF文件中可以帮助保持特定的顺序和布局&#xff0c;同时确保图像的质量不会因为格式转换而下降。它是免费&#xff0c;不限次数&#xff0c;批量导入也毫无压力。操作堪比发朋友圈&#xff1a;拖图进来 → 选个纸张尺寸 → 点击转换 → 指定保存路径&…

使用宝塔面板搭建 PHP 环境开发一个简单的 PHP 例子

目录一、引言二、准备工作2.1 服务器选择2.2 下载安装宝塔面板三、使用宝塔面板搭建 PHP 环境3.1 登录宝塔面板3.2 选择 Web Server3.3 安装 PHP3.4 安装 MySQL 数据库四、开发一个简单的 PHP 例子4.1 创建 PHP 文件4.2 编写 PHP 代码4.3 设置站点4.4 访问 PHP 页面五、常见问题…

AWS WebRTC:我们的业务模式

拉流、卡录基本流程 设备端&#xff08;摄像机&#xff09; 与 App端 是通过 AWS KVS WebRTC 信令服务进行“点对点连接”的&#xff0c;真正的媒体数据&#xff08;音视频&#xff09;是通过 WebRTC 的 ICE 通道&#xff08;P2P 或 TURN&#xff09;直接传输的&#xff0c;而不…

使用Python,OpenCV,K-Means聚类查找图像中最主要的颜色

使用Python&#xff0c;OpenCV&#xff0c;K-Means聚类查找图像中最主要的颜色 分别把跑图聚类选取1, 2, 3&#xff0c;4, 5, 6, 7&#xff0c;8, 9种主要颜色并绘制colormap颜色图; 效果图 分别把跑图聚类选取3&#xff0c;4, 5&#xff0c;7&#xff0c;9种主要颜色并绘制…

DBAPI 实现分页查询的两种方法

DBAPI 实现分页查询的两种方法 背景 在进行分页查询时&#xff0c;用户通常需要传入当前页码 pageNo 和每页显示的条数 pageSize 参数。根据这两个参数&#xff0c;我们可以从数据库中查询出当前页的数据。以 MySQL 为例&#xff0c;分页查询的 SQL 语句如下&#xff1a; se…

第五天上课 SSLPolicy策略和Network Discovery技术

SSL Policy场景1:拥有自家服务器的私钥&#xff0c;解密访问自家服务器的ssl流量场景2: 内部用户访问互联网的ssl流量&#xff0c;需要解密并重签名Correlation and Compliance相关性与合规性配置相关性与合规性策略&#xff0c;在10.1.1.0/24网络中&#xff0c;当通过Network …

进阶07:C#与通用OPC UA通信范例

本节目标&#xff1a; 1&#xff09;安装软件&#xff0c;搭建虚拟OPC UA服务器&#xff1b; 2&#xff09;使用UaExpert&#xff0c;读取OPC UA服务器中的变量&#xff1b; 3&#xff09;编写Winform程序&#xff0c;读写服务器中变量值&#xff0c;创建订阅触发事件&#…

大模型微调学习笔记(基于讯飞星辰MaaS速学版)

文章目录参考资料说明大模型微调入门微调简介微调步骤数据准备模型选择训练方式效果评估模型部署大模型微调&#xff08;基于讯飞星辰Maas&#xff09;构建数据集方法1&#xff1a;预置数据集方法2&#xff1a;创建数据集数据辅助工具数据集划分模型微调数据配置参数配置模型部…

[CSS]让overflow不用按shift可以滚轮水平滚动(纯CSS)

前言 我不爽前端无法直接滚轮横向滚动很久了 明明浏览器可以直接判断 x滚动且y不滚动的时候滚轮事件可以直接操作横向滚动 这个是我探究出来的方法,尤其适合这种很多很多小tag的情况解析 原理是将竖向排列的overflow旋转成横向,实际操作的还是竖向overflow.继而实现鼠标滚轮不用…

截稿倒计时 TrustCom‘25大会即将召开

会议资讯IEEE TrustCom-2025&#xff08;第24届IEEE计算与通信领域信任、安全与隐私国际会议&#xff09;是一个展示可信计算、通信、网络和机器学习领域前沿成果的学术平台。会议聚焦计算机系统、网络及人工智能在信任、安全、隐私、可靠性、可依赖性、生存性、可用性和容错性…

Day4.AndroidAudio初始化

1.AudioServer初始化 AudioServer 是 Android 音频系统的核心服务&#xff0c;负责管理音频硬件资源、音频策略调度、跨进程音频通信等核心功能。它由 Init 进程启动&#xff0c;是系统核心服务之一&#xff0c;直接影响音频播放、录音、音效处理等功能的正常运行。 1.1AudioSe…

OSPF 协议(多区域)

1. OSPF 单区域存在的问题① LSDB庞大&#xff0c;占用内存大&#xff0c;SPF计算开销大&#xff1b;② LSA洪泛范围大&#xff0c;拓扑变化影响范围大&#xff1b;③ 路由不能被汇总&#xff0c;路由表庞大&#xff0c;查找路由开销大。2. OSPF 多区域优点① 每个区域独立存储…

R 语言绘制六种精美热图:转录组数据可视化实践(基于 pheatmap 包)

在转录组 Bulk 测序数据分析中&#xff0c;热图是展示基因表达模式、样本聚类关系的核心可视化工具。一张高质量的热图不仅能清晰呈现数据特征&#xff0c;更能提升研究成果的展示效果。本文基于 R 语言的pheatmap包&#xff0c;整理了六种适用于不同场景的热图绘制方法&#x…

图片PDF识别工具:扫描PDF文件批量OCR区域图识别改名,识别大量PDF区域内容一次性改名

以下是使用“咕嘎批量OCR识别图片PDF多区域内容重命名导出表格系统”进行操作的具体步骤&#xff1a;1. 打开工具并获取区域坐标打开软件后&#xff0c;选择“PDF识别模式”。导入一个PDF文件作为样本&#xff0c;框选需要提取文字的区域&#xff0c;并保存区域坐标。如果有多个…

中国汽车能源消耗量(2010-2024年)

1419中国汽车能源消耗量&#xff08;2010-2024年&#xff09;发文主题分布数据来源中华人民共和国工业和信息化部-中国汽车能源消耗量查询中国汽车能源消耗量查询 (miit.gov.cn)时间跨度2010-2024年数据范围全国汽车企业数据指标本数据集包含包含传统汽车能源消耗量数据以及新能…

Python 实现服务器自动故障处理工具:从监控到自愈的完整方案

在服务器运维过程中,80% 的故障都是重复性的简单问题(如磁盘空间不足、内存泄漏、服务进程挂掉等)。本文将介绍如何使用 Python 开发一款轻量级自动故障处理工具,通过状态监控、异常诊断、自动修复三个核心模块,实现服务器常见故障的无人值守处理。 核心依赖库 psutil:跨…