基于RBAC模型的灵活权限控制
Pear框架权限校验系统详解:基于RBAC模型的灵活权限控制
前言
在现代Web应用开发中,权限控制是保障系统安全的核心机制。Pear框架提供了一套完整的权限校验系统,基于RBAC(Role-Based Access Control,基于角色的访问控制)模型设计,为开发者提供了灵活而强大的权限管理能力。
RBAC权限模型架构
Pear框架的权限系统采用经典的RBAC模型,通过四个核心实体构建权限体系:
- User(用户) - 系统的使用者
- Role(角色) - 权限的集合容器
- Permission(权限) - 最小的权限单元
- 关联关系 - User-Role(1:N)和 Role-Permission(N:N)的多对多关系
这种设计模式使得权限管理更加清晰和灵活易于扩展,管理员只需为用户分配适当的角色,而无需逐个配置权限。
核心组件设计
数据库表结构
Pear框架通过四个关键表实现权限管理:
- user表存储用户基本信息
- role表定义系统中的角色类型
- permission表存储具体的权限定义
- user_role_ass和role_permission_ass两个关联表处理多对多关系
DAO层与Service层扩展性设计
-
权限管理扩展性:
- PermissionDAO+RoleDAO:提供权限Permission与角色Role的增删改查基础操作;
- PermissionService+RoleService:在 DAO 基础上提供业务逻辑封装,处理角色与用户、用户与权限的多对多关系维护;
-
自由扩展机制:
开发者可以自由创建新的角色和权限,通过以下方式使用:
// 创建新角色 roleService.createRole("CUSTOM_ROLE", "自定义角色描述"); // 为角色分配权限 permissionService.addPermissions("CUSTOM_ROLE", "CUSTOM_PERMISSION_1", "CUSTOM_PERMISSION_2"); // 为用户分配角色 roleService.addUserRole(userId, "CUSTOM_ROLE");
权限缓存机制
为了提升系统性能,Pear框架实现了智能缓存机制。在系统级权限校验过程中,用户的权限集合会被存入缓存容器中,避免重复查询数据库。当权限或角色发生变化时,系统会自动清除相关缓存,确保权限数据的一致性;
@Overridepublic long[] getPermissionIds(long userId) {long[] permissionsIds;if (cacheContainer.contains(Constant.PEAR_PERMISSION_CACHE_NAME)) {permissionsIds = cacheContainer.get(Constant.PEAR_PERMISSION_CACHE_NAME, userId);if(permissionsIds != null&&permissionsIds.length!=0) return permissionsIds;}User user = userDAO.getUserById(userId);if (user == null) throw new AuthorizationException();long[] roleIds = roleDAO.getUserRole(user.getId());if (roleIds == null) return null;HashSet<Long> tempSet = new HashSet<>();for (long roleId : roleIds) {long[] permissionId = permissionDAO.getPermissions(roleId);if (permissionId != null) {for (long permission : permissionId) {tempSet.add(permission);}}}permissionsIds = tempSet.stream().mapToLong(Long::longValue).toArray();if (cacheContainer.contains(Constant.PEAR_PERMISSION_CACHE_NAME)) {cacheContainer.put(Constant.PEAR_PERMISSION_CACHE_NAME, userId, permissionsIds);}return permissionsIds;}
权限初始化与管理
在应用启动时,PearApplicationInitialization初始化类会自动创建系统所需的基础角色和权限:
预定义角色:
- ROLE_SUPERUSER - 超级管理员,拥有所有权限
- ROLE_STAFF - 员工,可访问后台系统和查询数据
- ROLE_CUSTOMER - 客户,无法登录后台系统
- ROLE_FINANCE - 财务,可读写除权限外的所有数据
- ROLE_SECRETARY - 秘书,可读写所有数据但不能修改管理员权限
预定义权限:
- ADMIN_CREATE/DELETE/UPDATE/SELECT - CRUD基础操作权限
- PERMISSION_WRITER - 写操作权限
- PERMISSION_SYNC - 锁定权限(仅推荐superuser使用)
- ADMIN_NULL - 无权限状态
// Pear基础角色与权限入库
permissionService.createPermission(Constant.ADMIN_CREATE,"ADMIN can be created.");
permissionService.createPermission(Constant.ADMIN_DELETE,"ADMIN can be deleted.");
permissionService.createPermission(Constant.ADMIN_UPDATE,"ADMIN can be updated.");
permissionService.createPermission(Constant.ADMIN_SELECT,"ADMIN can be selected.");
permissionService.createPermission(Constant.PERMISSION_WRITER,"Writable operation permission.");
permissionService.createPermission(Constant.PERMISSION_SYNC,"The current permission group is locked and cannot be operated on.");
permissionService.createPermission(Constant.ADMIN_NULL,"An empty operation indicates no operation permission at all.");
roleService.createRole(Constant.ROLE_SUPERUSER,"Super Administrator, with all permissions.");
roleService.createRole(Constant.ROLE_STAFF,"Employees have the permission to access the background system and query data.");
roleService.createRole(Constant.ROLE_CUSTOMER,"Customers cannot log in to the background system.");
roleService.createRole(Constant.ROLE_FINANCE,"The finance department can access the background system and read and write all data except for permissions.");
roleService.createRole(Constant.ROLE_SECRETARY,"The secretary can access the background and read and write all data, but cannot modify the administrator's permissions.");
permissionService.addPermissions(Constant.ROLE_SUPERUSER, Constant.ADMIN_CREATE, Constant.ADMIN_DELETE, Constant.ADMIN_UPDATE, Constant.ADMIN_SELECT, Constant.PERMISSION_WRITER);
permissionService.addPermissions(Constant.ROLE_STAFF, Constant.ADMIN_SELECT);
permissionService.addPermissions(Constant.ROLE_CUSTOMER, Constant.ADMIN_NULL);
permissionService.addPermissions(Constant.ROLE_FINANCE, Constant.ADMIN_SELECT, Constant.ADMIN_UPDATE, Constant.ADMIN_CREATE, Constant.ADMIN_DELETE);
permissionService.addPermissions(Constant.ROLE_SUPERUSER, Constant.ADMIN_CREATE, Constant.ADMIN_DELETE, Constant.ADMIN_UPDATE, Constant.ADMIN_SELECT, Constant.PERMISSION_WRITER);
通过命令行参数创建的超级用户会自动获得ROLE_SUPERUSER角色,确保系统管理员拥有最高权限。
双层权限校验机制
Pear框架设计了双层权限校验机制,通过[@Verification](file:///Applications/LocalGit/pear-spring-boot-starter/pear-spring-boot-core/src/main/java/cn/muzisheng/pear/annotation/Verification.java#L11-L20)注解进行控制:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Verification {// 是否启用系统级权限校验,对自动生成的CRUD方法按照AdminObject的permissions字段进行权限校验,默认为启用boolean SystemVerify() default true;// 是否启用用户级权限校验,调用AccessCheck钩子函数,默认为启用boolean UserVerify() default true;// 权限String Permission() default Constant.ADMIN_NULL;
}// 示例如下:
@Verification(Permission = Constant.ADMIN_SELECT)
public ResponseEntity<Result<Object>> handleQueryOrGetOne(...) {...}
前置数据处理
-
切点定义与拦截
@Pointcut("@annotation(cn.muzisheng.pear.annotation.Verification)") public void verificationPointcut() { }
HookAspect 通过切点表达式拦截所有带有 @Verification 注解的方法;
-
权限校验核心流程
通过循环通知对@Verification注解的方法织入业务逻辑有如下数据处理:
-
参数提取,从被拦截的方法中提取 @Verification 注解和必要的方法参数;
Method method = ((MethodSignature) jp.getSignature()).getMethod(); Verification verification = method.getAnnotation(Verification.class); // 提取 HttpServletRequest 和 AdminObject 参数
对于AdminObject是 Pear 框架的核心元数据容器,通过注解扫描实体类生成。主要功能有:
- 存储实体类的结构信息(字段、类型、约束等)
- 通过注解属性填充的字段扩展信息,如该字段是否为唯一键、是否可排序可查询等;
在权限模块中的作用是作为用户级权限校验的载体,提供 AccessCheck 钩子函数,允许开发者为特定实体类定制权限校验逻辑。
设计建议,当脱离 Pear 框架时可以将 @Verification 注解改造为:
@Verification(permission = "ADMIN_SELECT",userVerifyMethod = "customAccessCheck" // 指定用户级校验方法名 )
然后在 HookAspect 中通过反射调用指定方法实现用户级权限校验,这样也可以提供灵活的权限控制方式。
-
获取当前用户身份
// 获取当前用户 User user = userService.currentUser(request); if (user == null) {String signUrl = configService.getValue(Constant.KEY_SITE_SIGNIN_URL);if (signUrl == null) {throw new AuthorizationException("unauthorized");} else {throw new AuthorizationException("unauthorized;signUrl=" + signUrl);} }
从Request会话中获取user对象,这个存储机制是由Pear框架的JWT+Session控制的。
-
系统级权限校验
基于用户角色和权限进行校验,通过查询用户拥有的所有角色及其对应的权限集合,验证用户是否具备执行操作的权限。
/*** 系统级校验用户是否登录,未登录则抛出异常** @param verification 注解* @param user 当前用户* @throws ForbiddenException 用户权限不足**/private boolean withAdminAuth(Verification verification, User user) {String permissionCode = verification.Permission();if (permissionCode.equals(Constant.ADMIN_NULL)) return true;Permission permission = permissionService.getPermissionByCode(permissionCode);return permissionService.hasPermission(user.getId(), permission.getId());}
用户级权限校验
通过自定义的[AccessCheck](file:///Applications/LocalGit/pear-spring-boot-starter/pear-spring-boot-core/src/main/java/cn/muzisheng/pear/handler/AccessCheck.java#L6-L17)钩子函数实现,允许开发者实现复杂的业务逻辑权限控制。这种设计使得权限校验不仅限于静态的角色权限匹配,还能根据具体业务场景进行动态判断。
/*** 用户级校验身份,校验不通过则抛出异常** @param adminObject 通用类对象* @param request 请求* @param user 当前用户* @throws ForbiddenException 身份校验不通过**/private boolean accessCheck(AdminObject adminObject, HttpServletRequest request, User user) {if (adminObject.getAccessCheck() != null) {try {return adminObject.getAccessCheck().execute(request, adminObject, user);} catch (Exception e) {throw new ForbiddenException(e.getMessage());}}return true;}
权限业务逻辑判断
通过标志符allow控制两层权限校验的结果,必须全部允许才可以执行原方法,否则抛出自定义异常,交由异常处理器返回对应401无权限状态码以及提示信息;
boolean allow = true;// 系统级校验if (verification.SystemVerify()) {allow = withAdminAuth(verification, user);}// 用户级校验if (verification.UserVerify() && adminObject != null && allow) {allow = accessCheck(adminObject, request, user);}if (!allow) throw new ForbiddenException("forbidden");return jp.proceed();
总结
权限校验系统各层功能整理:
- DAO层(数据访问层):
- RoleDAO 和 PermissionDAO 负责与数据库直接交互;
- 提供基础的增删改查操作,如 createRole、deletePermission、getPermissions 等;
- 处理用户、角色、权限之间的关联关系维护;
- 屏蔽底层数据库操作细节,为Service层提供统一数据访问接口;
- Service层(业务逻辑层):
- RoleServiceImpl 和 PermissionServiceImpl 实现具体业务逻辑;
- 在DAO层基础上提供更复杂的操作,如角色权限批量管理;
- 集成缓存机制(通过Pear内置的缓存容器 CacheContainer)提升性能;
- 处理业务规则验证,如保护系统默认角色不被误删;
- 协调多个DAO操作完成复杂业务流程;
- 注解(Annotation):
- @Verification 作为权限校验的标记点;
- 通过 SystemVerify 和 UserVerify 参数控制校验级别;
- Permission 参数指定需要的具体权限代码;
- 实现声明式权限控制,降低代码耦合度;
- 切面(Aspect):
- HookAspect 通过AOP机制拦截带 @Verification 注解的方法;
- 统一处理权限校验逻辑,避免业务代码重复;
- 提供系统级(基于RBAC)和用户级(自定义钩子)双重校验;
- 记录权限校验日志,便于审计和调试;
- 数据库表:
- user、role、permission 存储核心权限数据;
- user_role_ass、role_permission_ass 处理多对多关联关系;
- 通过标准化表结构支持RBAC权限模型;
- 为DAO层提供数据存储基础,支撑整个权限系统运行;
Pear框架的权限校验系统通过RBAC模型、双层校验机制和智能缓存策略,为开发者提供了一套完整、高效且易于扩展的权限管理解决方案。无论是简单的角色权限控制,还是复杂的业务逻辑校验,都能得到良好的支持,大大提升了Web应用的安全性和开发效率。
后言
欢迎各位在github上star作者开源的个人项目单体应用快速开发框架Pear(若点击不可跳转请手动输入地址 https://github.com/MuziSuper/pear-spring-boot-starter),对于此模块设计中出现的bug以及性能漏洞也会在之后不断修复,本博客旨在为框架设计高扩展易拔插的权限系统提供一种研发方向与案例。