目录
- RBAC3 模型核心概念
- 数据库表结构设计(完整RBAC3 + 审计)
- 工作流程详解
- Java 实现示例(含详细中文注释)
- API 使用示例
- 完整审计体系实现
- 测试用例验证
- 性能优化建议
一、RBAC3 模型核心概念
1.1 什么是 RBAC3?
RBAC3(Role-Based Access Control Level 3)是 RBAC 模型的完整版本,同时包含 RBAC1 的角色继承特性和 RBAC2 的约束机制,提供最全面的权限控制能力:
- RBAC1 特性:角色继承(层次化权限管理)
- RBAC2 特性:约束机制(互斥、基数、先决条件等)
- 组合优势:既有继承的灵活性,又有约束的安全性
1.2 RBAC3 核心能力矩阵
| 能力 | RBAC0 | RBAC1 | RBAC2 | RBAC3 |
|---|---|---|---|---|
| 用户-角色-权限 | ✓ | ✓ | ✓ | ✓ |
| 角色继承 | ✗ | ✓ | ✗ | ✓ |
| 角色互斥 | ✗ | ✗ | ✓ | ✓ |
| 基数约束 | ✗ | ✗ | ✓ | ✓ |
| 先决条件 | ✗ | ✗ | ✓ | ✓ |
| 完整审计 | ✗ | △ | △ | ✓ |
1.3 形象类比
现代化企业权限管控体系:
- 角色继承:实习生 → 开发工程师 → 高级工程师 → 技术经理 → 技术总监
- 每个上级角色自动继承下级角色的所有权限
- 约束机制:
- 互斥约束:财务审批角色和财务执行角色互斥
- 基数约束:CEO角色只能分配给1个人,系统管理员最多3人
- 先决条件:要成为技术总监,必须先担任技术经理至少2年
这种组合既保证了权限管理的效率(继承),又确保了安全合规(约束)
1.4 适用场景
- 大型企业应用:复杂的组织架构和权限需求
- 金融系统:严格的职责分离和合规要求
- 政府系统:多层级权限和安全审计
- SaaS平台:多租户环境下的精细化权限控制
二、数据库表结构设计(完整RBAC3 + 审计)
2.1 完整表结构总览
| 表名 | 说明 | 关系 |
|---|---|---|
sys_user | 用户表 | - |
sys_role | 角色表(含继承层级) | - |
sys_role_hierarchy | 角色继承关系表 | 父角色 ↔ 子角色 |
sys_permission | 权限表 | - |
sys_user_role | 用户-角色关联表 | 用户 ↔ 角色 |
sys_role_permission | 角色-权限关联表 | 角色 ↔ 权限 |
sys_role_constraint | 角色约束表 | 定义各种约束规则 |
sys_constraint_violation_log | 约束违规日志表 | 记录约束违规尝试 |
sys_audit_log | 完整审计日志表 | 记录所有关键操作 |
sys_audit_log_detail | 审计日志详情表 | 存储复杂操作详情 |
2.2 完整表结构定义
2.2.1 用户表 (sys_user)
CREATE TABLE `sys_user` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名(唯一)',
`password` VARCHAR(100) NOT NULL COMMENT '密码(加密存储)',
`real_name` VARCHAR(50) NOT NULL COMMENT '真实姓名',
`email` VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
`phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号',
`status` TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
2.2.2 角色表 (sys_role)
CREATE TABLE `sys_role` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_code` VARCHAR(50) NOT NULL COMMENT '角色编码(唯一)',
`role_name` VARCHAR(50) NOT NULL COMMENT '角色名称',
`description` VARCHAR(200) DEFAULT NULL COMMENT '角色描述',
`status` TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
`level` INT DEFAULT 0 COMMENT '角色层级(0-根角色)',
`max_users` INT DEFAULT NULL COMMENT '最大用户数(基数约束)',
`min_prerequisite_roles` INT DEFAULT 0 COMMENT '最少前置角色数',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_code` (`role_code`),
KEY `idx_level` (`level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表(支持继承和约束)';
2.2.3 角色继承关系表 (sys_role_hierarchy)
CREATE TABLE `sys_role_hierarchy` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '关系ID',
`parent_role_id` BIGINT NOT NULL COMMENT '父角色ID',
`child_role_id` BIGINT NOT NULL COMMENT '子角色ID',
`hierarchy_level` INT NOT NULL COMMENT '继承层级(1-直接,2-间接...)',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_parent_child` (`parent_role_id`, `child_role_id`),
KEY `idx_parent_role_id` (`parent_role_id`),
KEY `idx_child_role_id` (`child_role_id`),
CONSTRAINT `chk_no_self_reference` CHECK (`parent_role_id` != `child_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色继承关系表(闭包表)';
2.2.4 权限表 (sys_permission)
CREATE TABLE `sys_permission` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`permission_code` VARCHAR(100) NOT NULL COMMENT '权限编码(唯一)',
`permission_name` VARCHAR(50) NOT NULL COMMENT '权限名称',
`resource_type` VARCHAR(20) NOT NULL COMMENT '资源类型:MENU/BUTTON/API',
`resource_path` VARCHAR(200) DEFAULT NULL COMMENT '资源路径',
`description` VARCHAR(200) DEFAULT NULL COMMENT '权限描述',
`status` TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_permission_code` (`permission_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';
2.2.5 用户-角色关联表 (sys_user_role)
CREATE TABLE `sys_user_role` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '关联ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`role_id` BIGINT NOT NULL COMMENT '角色ID',
`assign_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '分配时间',
`assign_operator_id` BIGINT NOT NULL COMMENT '分配操作人ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_role` (`user_id`, `role_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户-角色关联表';
2.2.6 角色-权限关联表 (sys_role_permission)
CREATE TABLE `sys_role_permission` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '关联ID',
`role_id` BIGINT NOT NULL COMMENT '角色ID',
`permission_id` BIGINT NOT NULL COMMENT '权限ID',
`assign_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '分配时间',
`assign_operator_id` BIGINT NOT NULL COMMENT '分配操作人ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_permission` (`role_id`, `permission_id`),
KEY `idx_role_id` (`role_id`),
KEY `idx_permission_id` (`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色-权限关联表';
2.2.7 角色约束表 (sys_role_constraint) - RBAC2核心
CREATE TABLE `sys_role_constraint` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '约束ID',
`constraint_type` VARCHAR(30) NOT NULL COMMENT '约束类型:MUTUAL_EXCLUSION/CARDINALITY/PREREQUISITE',
`constraint_subtype` VARCHAR(30) DEFAULT NULL COMMENT '约束子类型:STATIC/DYNAMIC',
`primary_role_id` BIGINT NOT NULL COMMENT '主角色ID',
`secondary_role_id` BIGINT DEFAULT NULL COMMENT '次角色ID',
`cardinality_limit` INT DEFAULT NULL COMMENT '基数限制值',
`constraint_scope` VARCHAR(20) NOT NULL COMMENT '约束范围:USER/ROLE',
`description` VARCHAR(200) DEFAULT NULL COMMENT '约束描述',
`status` TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_primary_role` (`primary_role_id`),
KEY `idx_secondary_role` (`secondary_role_id`),
KEY `idx_constraint_type` (`constraint_type`),
CONSTRAINT `chk_mutual_exclusion_roles` CHECK (
(`constraint_type` = 'MUTUAL_EXCLUSION' AND `secondary_role_id` IS NOT NULL) OR
(`constraint_type` != 'MUTUAL_EXCLUSION')
),
CONSTRAINT `chk_cardinality_limit` CHECK (
(`constraint_type` = 'CARDINALITY' AND `cardinality_limit` IS NOT NULL) OR
(`constraint_type` != 'CARDINALITY')
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色约束表(RBAC2约束机制)';
2.2.8 完整审计日志表 (sys_audit_log) - 增强版
CREATE TABLE `sys_audit_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '审计日志ID',
`operation_type` VARCHAR(20) NOT NULL COMMENT '操作类型:CREATE/UPDATE/DELETE/ASSIGN/REVOKE/VIOLATION',
`target_type` VARCHAR(20) NOT NULL COMMENT '目标类型:USER/ROLE/PERMISSION/HIERARCHY/CONSTRAINT',
`target_id` BIGINT NOT NULL COMMENT '目标ID',
`operator_id` BIGINT NOT NULL COMMENT '操作人ID',
`operator_name` VARCHAR(50) NOT NULL COMMENT '操作人姓名',
`operation_details_id` BIGINT DEFAULT NULL COMMENT '操作详情ID(关联详情表)',
`ip_address` VARCHAR(45) DEFAULT NULL COMMENT '操作IP地址',
`user_agent` VARCHAR(500) DEFAULT NULL COMMENT '用户代理',
`session_id` VARCHAR(100) DEFAULT NULL COMMENT '会话ID',
`risk_level` VARCHAR(10) DEFAULT 'MEDIUM' COMMENT '风险等级:LOW/MEDIUM/HIGH/CRITICAL',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
PRIMARY KEY (`id`),
KEY `idx_operator_id` (`operator_id`),
KEY `idx_target_type_id` (`target_type`, `target_id`),
KEY `idx_create_time` (`create_time`),
KEY `idx_risk_level` (`risk_level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='完整审计日志表(RBAC3增强)';
2.2.9 审计日志详情表 (sys_audit_log_detail) - 新增
CREATE TABLE `sys_audit_log_detail` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '详情ID',
`before_state` JSON DEFAULT NULL COMMENT '操作前状态',
`after_state` JSON DEFAULT NULL COMMENT '操作后状态',
`additional_info` JSON DEFAULT NULL COMMENT '额外信息',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志详情表(存储复杂操作详情)';
2.2.10 约束违规日志表 (sys_constraint_violation_log)
CREATE TABLE `sys_constraint_violation_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '违规日志ID',
`constraint_id` BIGINT NOT NULL COMMENT '违反的约束ID',
`constraint_type` VARCHAR(30) NOT NULL COMMENT '约束类型',
`user_id` BIGINT NOT NULL COMMENT '违规用户ID',
`attempted_operation` VARCHAR(50) NOT NULL COMMENT '尝试的操作',
`violation_details` JSON DEFAULT NULL COMMENT '违规详情',
`ip_address` VARCHAR(45) DEFAULT NULL COMMENT '操作IP地址',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '违规时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_constraint_id` (`constraint_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='约束违规日志表';
2.3 示例数据(完整RBAC3场景)
组织架构数据
-- 角色层次:员工 → 经理 → 总监
INSERT INTO sys_role (role_code, role_name, description, level) VALUES
('EMPLOYEE', '普通员工', '基础员工角色', 0),
('MANAGER', '部门经理', '部门管理角色', 1),
('DIRECTOR', '部门总监', '部门总监角色', 2);
-- 角色继承关系
INSERT INTO sys_role_hierarchy (parent_role_id, child_role_id, hierarchy_level) VALUES
(1, 2, 1), -- 经理继承员工
(2, 3, 1), -- 总监继承经理
(1, 3, 2); -- 总监间接继承员工
-- 互斥角色:会计和出纳
INSERT INTO sys_role (role_code, role_name, description, level) VALUES
('ACCOUNTANT', '会计', '财务管理角色', 0),
('CASHIER', '出纳', '现金管理角色', 0);
-- 互斥约束
INSERT INTO sys_role_constraint (constraint_type, constraint_subtype, primary_role_id, secondary_role_id, constraint_scope, description) VALUES
('MUTUAL_EXCLUSION', 'STATIC', 4, 5, 'USER', '会计和出纳角色互斥'),
('MUTUAL_EXCLUSION', 'STATIC', 5, 4, 'USER', '出纳和会计角色互斥');
-- 基数约束:总监最多2人
INSERT INTO sys_role_constraint (constraint_type, constraint_scope, primary_role_id, cardinality_limit, description) VALUES
('CARDINALITY', 'ROLE', 3, 2, '总监角色最多2人');
-- 先决条件:总监需要先有经理角色
INSERT INTO sys_role_constraint (constraint_type, constraint_scope, primary_role_id, secondary_role_id, description) VALUES
('PREREQUISITE', 'USER', 3, 2, '总监角色需要先拥有经理角色');
三、RBAC3 工作流程详解
3.1 完整权限验证流程
3.2 角色分配流程(带完整约束检查)
3.3 核心验证逻辑
3.3.1 完整角色集计算
- 获取用户直接关联的角色
- 递归查询所有祖先角色(通过闭包表)
- 合并直接角色 + 所有祖先角色,去重
- 返回完整角色集
3.3.2 多层约束检查
- 第一层:互斥约束(静态)
- 第二层:基数约束(用户级 + 角色级)
- 第三层:先决条件约束
- 第四层:继承关系约束(防止循环继承)
3.3.3 审计日志记录
- 成功操作:记录操作详情、风险等级
- 失败操作:记录违规详情、尝试信息
- 敏感操作:记录完整前后状态对比
四、Java 实现示例(含详细中文注释)
4.1 RBAC3权限服务核心实现
/**
* RBAC3权限服务实现类(完整版)
* 集成RBAC1角色继承 + RBAC2约束机制 + 完整审计
*/
@Service
@Slf4j
public class Rbac3PermissionService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private RoleHierarchyMapper roleHierarchyMapper;
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private Rbac2ConstraintService constraintService;
@Autowired
private AuditLogService auditLogService;
/**
* 检查用户是否拥有指定权限(RBAC3完整验证)
*
* 验证流程:
* 1. 计算用户完整角色集(直接角色 + 所有继承角色)
* 2. 查询所有角色的权限
* 3. 检查目标权限是否存在
* 4. 记录审计日志(成功/失败)
*
* @param userId 用户ID
* @param permissionCode 目标权限编码
* @param requestContext 请求上下文(用于审计)
* @return true-拥有权限,false-无权限
*/
public boolean hasPermission(Long userId, String permissionCode,
RequestContext requestContext) {
log.info("开始RBAC3权限检查: userId={}, permissionCode={}", userId, permissionCode);
try {
// 1. 获取用户完整角色集(含继承)
Set<Long> allRoleIds = getUserAllRoleIds(userId);
if (allRoleIds.isEmpty()) {
log.warn("用户没有分配任何角色,userId={}", userId);
auditPermissionCheck(userId, permissionCode, false, requestContext, "用户无角色");
return false;
}
// 2. 获取所有角色的权限
List<Long> permissionIds = roleMapper.selectPermissionIdsByRoleIds(new ArrayList<>(allRoleIds));
if (permissionIds.isEmpty()) {
log.warn("角色没有分配任何权限,roleIds={}", allRoleIds);
auditPermissionCheck(userId, permissionCode, false, requestContext, "角色无权限");
return false;
}
// 3. 检查权限是否存在
List<Permission> permissions = permissionMapper.selectByIds(permissionIds);
Set<String> permissionCodes = permissions.stream()
.map(Permission::getPermissionCode)
.collect(Collectors.toSet());
boolean hasPermission = permissionCodes.contains(permissionCode);
// 4. 记录审计日志
String auditReason = hasPermission ? "权限检查通过" : "权限检查失败";
auditPermissionCheck(userId, permissionCode, hasPermission, requestContext, auditReason);
log.info("RBAC3权限检查结果: userId={}, permissionCode={}, result={}",
userId, permissionCode, hasPermission);
return hasPermission;
} catch (Exception e) {
log.error("RBAC3权限检查发生异常", e);
auditPermissionCheck(userId, permissionCode, false, requestContext, "系统异常: " + e.getMessage());
return false;
}
}
/**
* 获取用户完整角色集(直接角色 + 所有祖先角色)
* 这是RBAC1继承机制的核心实现
*
* @param userId 用户ID
* @return 完整角色ID集合
*/
private Set<Long> getUserAllRoleIds(Long userId) {
// 1. 获取用户直接关联的角色
List<Long> directRoleIds = userMapper.selectRoleIdsByUserId(userId);
if (CollectionUtils.isEmpty(directRoleIds)) {
return Collections.emptySet();
}
// 2. 获取所有祖先角色(通过闭包表)
Set<Long> allRoleIds = new HashSet<>(directRoleIds);
for (Long directRoleId : directRoleIds) {
List<Long> ancestorRoleIds = roleHierarchyMapper.selectAllAncestorRoleIdsByChildId(directRoleId);
allRoleIds.addAll(ancestorRoleIds);
}
log.debug("用户完整角色集: userId={}, roleIds={}", userId, allRoleIds);
return allRoleIds;
}
/**
* 记录权限检查审计日志
*/
private void auditPermissionCheck(Long userId, String permissionCode,
boolean success, RequestContext context, String reason) {
try {
// 构建操作详情
Map<String, Object> details = new HashMap<>();
details.put("userId", userId);
details.put("permissionCode", permissionCode);
details.put("success", success);
details.put("reason", reason);
// 确定风险等级
String riskLevel = "MEDIUM";
if (permissionCode != null && (permissionCode.contains("DELETE") ||
permissionCode.contains("ADMIN") || permissionCode.contains("FINANCE"))) {
riskLevel = success ? "HIGH" : "CRITICAL";
}
// 记录审计日志
auditLogService.logOperation(
success ? "ACCESS_SUCCESS" : "ACCESS_DENIED",
"PERMISSION",
userId,
context.getOperatorId(),
context.getIpAddress(),
context.getUserAgent(),
context.getSessionId(),
riskLevel,
details
);
} catch (Exception e) {
log.error("记录权限检查审计日志失败", e);
}
}
/**
* 分配角色给用户(RBAC3完整约束检查)
*
* @param userId 用户ID
* @param roleId 角色ID
* @param operatorId 操作人ID
* @param requestContext 请求上下文
* @throws ConstraintViolationException 违反约束时抛出
*/
@Transactional(rollbackFor = Exception.class)
public void assignRoleToUser(Long userId, Long roleId, Long operatorId,
RequestContext requestContext) throws ConstraintViolationException {
log.info("开始RBAC3角色分配: userId={}, roleId={}, operatorId={}", userId, roleId, operatorId);
try {
// 1. 执行RBAC2约束检查(互斥、基数、先决条件)
constraintService.checkRoleAssignmentConstraints(userId, roleId, operatorId, requestContext);
// 2. 检查继承关系约束(防止循环继承等)
checkInheritanceConstraints(userId, roleId);
// 3. 执行角色分配
UserRole userRole = UserRole.builder()
.userId(userId)
.roleId(roleId)
.assignOperatorId(operatorId)
.build();
userMapper.insertUserRole(userRole);
// 4. 更新继承关系缓存(如果使用缓存)
updateInheritanceCache(userId);
// 5. 记录成功审计日志
Map<String, Object> details = new HashMap<>();
details.put("userId", userId);
details.put("roleId", roleId);
details.put("operation", "ASSIGN_ROLE");
auditLogService.logOperation(
"ASSIGN",
"USER_ROLE",
userId,
operatorId,
requestContext.getIpAddress(),
requestContext.getUserAgent(),
requestContext.getSessionId(),
"MEDIUM",
details
);
log.info("RBAC3角色分配成功: userId={}, roleId={}", userId, roleId);
} catch (ConstraintViolationException e) {
// 记录约束违规审计日志
log.warn("RBAC3角色分配违反约束: {}", e.getMessage());
auditConstraintViolation(userId, roleId, operatorId, requestContext, e);
throw e;
} catch (Exception e) {
log.error("RBAC3角色分配发生异常", e);
auditOperationFailure(userId, roleId, operatorId, requestContext, e);
throw new BusinessException("角色分配失败", e);
}
}
/**
* 检查继承关系约束
* 确保角色分配不会破坏继承关系的一致性
*/
private void checkInheritanceConstraints(Long userId, Long roleId) {
// 示例:检查是否违反继承层级约束
// 例如:不能直接分配总监角色给没有经理角色的用户(如果配置了相关约束)
// 获取角色的继承层级
Role role = roleMapper.selectById(roleId);
if (role.getLevel() > 1) {
// 高层级角色需要检查继承路径
List<Long> requiredAncestorRoles = getRequiredAncestorRoles(roleId);
List<Long> userRoles = userMapper.selectRoleIdsByUserId(userId);
Set<Long> userRoleSet = new HashSet<>(userRoles);
for (Long requiredRole : requiredAncestorRoles) {
if (!userRoleSet.contains(requiredRole)) {
throw new ConstraintViolationException(
"分配高层级角色需要先拥有必要的祖先角色",
null,
"INHERITANCE",
buildInheritanceViolationDetails(userId, roleId, requiredRole)
);
}
}
}
}
/**
* 获取角色必需的祖先角色
*/
private List<Long> getRequiredAncestorRoles(Long roleId) {
// 简化实现:返回直接父角色
return roleHierarchyMapper.selectDirectParentRoleIds(roleId);
}
/**
* 构建继承约束违规详情
*/
private String buildInheritanceViolationDetails(Long userId, Long roleId, Long requiredRole) {
Map<String, Object> details = new HashMap<>();
details.put("userId", userId);
details.put("targetRoleId", roleId);
details.put("requiredAncestorRoleId", requiredRole);
details.put("reason", "缺少必需的祖先角色");
return JsonUtils.toJson(details);
}
/**
* 更新继承关系缓存
*/
private void updateInheritanceCache(Long userId) {
// 如果使用缓存,这里需要更新用户的角色缓存
// cacheService.evictUserRoles(userId);
}
/**
* 记录约束违规审计日志
*/
private void auditConstraintViolation(Long userId, Long roleId, Long operatorId,
RequestContext context, ConstraintViolationException e) {
try {
Map<String, Object> details = new HashMap<>();
details.put("userId", userId);
details.put("roleId", roleId);
details.put("constraintType", e.getConstraintType());
details.put("violationDetails", e.getViolationDetails());
auditLogService.logOperation(
"VIOLATION",
"CONSTRAINT",
e.getConstraintId() != null ? e.getConstraintId() : -1L,
operatorId,
context.getIpAddress(),
context.getUserAgent(),
context.getSessionId(),
"CRITICAL",
details
);
} catch (Exception ex) {
log.error("记录约束违规审计日志失败", ex);
}
}
/**
* 记录操作失败审计日志
*/
private void auditOperationFailure(Long userId, Long roleId, Long operatorId,
RequestContext context, Exception e) {
try {
Map<String, Object> details = new HashMap<>();
details.put("userId", userId);
details.put("roleId", roleId);
details.put("error", e.getMessage());
details.put("stackTrace", e.getClass().getName());
auditLogService.logOperation(
"OPERATION_FAILED",
"USER_ROLE",
userId,
operatorId,
context.getIpAddress(),
context.getUserAgent(),
context.getSessionId(),
"HIGH",
details
);
} catch (Exception ex) {
log.error("记录操作失败审计日志失败", ex);
}
}
}
4.2 完整审计服务实现
/**
* 完整审计日志服务(RBAC3增强版)
* 支持复杂操作详情存储和风险等级评估
*/
@Service
@Slf4j
public class AuditLogService {
@Autowired
private AuditLogMapper auditLogMapper;
@Autowired
private AuditLogDetailMapper auditLogDetailMapper;
@Autowired
private UserMapper userMapper;
/**
* 记录完整审计日志(支持复杂详情)
*
* @param operationType 操作类型
* @param targetType 目标类型
* @param targetId 目标ID
* @param operatorId 操作人ID
* @param ipAddress IP地址
* @param userAgent 用户代理
* @param sessionId 会话ID
* @param riskLevel 风险等级
* @param operationDetails 操作详情
*/
public void logOperation(String operationType, String targetType, Long targetId,
Long operatorId, String ipAddress, String userAgent,
String sessionId, String riskLevel, Object operationDetails) {
try {
// 1. 获取操作人信息
User operator = userMapper.selectById(operatorId);
String operatorName = operator != null ? operator.getRealName() : "未知用户";
// 2. 处理复杂操作详情(超过一定大小存储到详情表)
Long detailId = null;
String simpleDetails = null;
if (operationDetails != null) {
String detailsJson = JsonUtils.toJson(operationDetails);
if (detailsJson.length() > 1000) {
// 大详情存储到详情表
AuditLogDetail detail = AuditLogDetail.builder()
.beforeState(null) // 可以根据需要设置
.afterState(operationDetails)
.additionalInfo(Map.of("operationType", operationType))
.build();
auditLogDetailMapper.insert(detail);
detailId = detail.getId();
} else {
// 小详情直接存储
simpleDetails = detailsJson;
}
}
// 3. 创建主审计日志
AuditLog auditLog = AuditLog.builder()
.operationType(operationType)
.targetType(targetType)
.targetId(targetId)
.operatorId(operatorId)
.operatorName(operatorName)
.operationDetailsId(detailId)
.ipAddress(ipAddress)
.userAgent(userAgent)
.sessionId(sessionId)
.riskLevel(riskLevel)
.build();
auditLogMapper.insert(auditLog);
log.debug("审计日志记录成功: operator={}, operation={}, riskLevel={}",
operatorName, operationType, riskLevel);
} catch (Exception e) {
log.error("记录审计日志失败", e);
// 审计日志失败不应影响主业务流程
}
}
/**
* 获取审计日志详情(合并主表和详情表)
*/
public AuditLogWithDetails getAuditLogWithDetails(Long auditLogId) {
AuditLog auditLog = auditLogMapper.selectById(auditLogId);
if (auditLog == null) {
return null;
}
AuditLogWithDetails result = new AuditLogWithDetails();
result.setAuditLog(auditLog);
if (auditLog.getOperationDetailsId() != null) {
AuditLogDetail detail = auditLogDetailMapper.selectById(auditLog.getOperationDetailsId());
result.setDetail(detail);
}
return result;
}
/**
* 查询高风险审计日志(用于安全监控)
*/
public List<AuditLog> getHighRiskAuditLogs(LocalDateTime startTime) {
return auditLogMapper.selectByRiskLevelAndTime("HIGH", "CRITICAL", startTime);
}
}
五、API 使用示例
5.1 完整RBAC3权限检查API
/**
* RBAC3权限验证Controller
*/
@RestController
@RequestMapping("/api/rbac3")
public class Rbac3Controller {
@Autowired
private Rbac3PermissionService permissionService;
/**
* 检查用户权限(完整RBAC3验证)
*
* 请求示例:
* GET /api/rbac3/has-permission?userId=1001&permissionCode=FINANCE_APPROVE
*
* 验证流程:
* 1. 计算用户完整角色集(含继承)
* 2. 检查基础权限
* 3. 检查运行时约束
* 4. 返回详细结果
*/
@GetMapping("/has-permission")
public Result<PermissionCheckResult> checkPermission(
@RequestParam Long userId,
@RequestParam String permissionCode,
HttpServletRequest request) {
RequestContext context = buildRequestContext(request);
boolean hasPermission = permissionService.hasPermission(userId, permissionCode, context);
PermissionCheckResult result = PermissionCheckResult.builder()
.userId(userId)
.permissionCode(permissionCode)
.hasPermission(hasPermission)
.checkTime(LocalDateTime.now())
.build();
return Result.success(result);
}
/**
* 分配角色给用户(带完整约束检查)
*/
@PostMapping("/assign-role")
public Result<Void> assignRole(@RequestBody @Valid AssignRoleRequest request,
HttpServletRequest httpRequest) {
RequestContext context = buildRequestContext(httpRequest);
Long currentUserId = getCurrentUserId(httpRequest);
try {
permissionService.assignRoleToUser(
request.getUserId(),
request.getRoleId(),
currentUserId,
context
);
return Result.success();
} catch (ConstraintViolationException e) {
return Result.error(ResultCode.CONSTRAINT_VIOLATION, e.getMessage())
.addData("constraintType", e.getConstraintType())
.addData("violationDetails", e.getViolationDetails());
}
}
/**
* 构建请求上下文(用于审计)
*/
private RequestContext buildRequestContext(HttpServletRequest request) {
return RequestContext.builder()
.ipAddress(getClientIpAddress(request))
.userAgent(request.getHeader("User-Agent"))
.sessionId(request.getSession().getId())
.build();
}
}
5.2 审计日志查询API
/**
* 审计日志查询Controller(RBAC3增强版)
*/
@RestController
@RequestMapping("/api/audit")
public class Rbac3AuditController {
@Autowired
private AuditLogService auditLogService;
/**
* 分页查询审计日志(支持风险等级过滤)
*/
@GetMapping("/logs")
public Result<PageResult<AuditLogWithDetails>> getAuditLogs(
@RequestParam(required = false) String riskLevel,
@RequestParam(defaultValue = "0") Long startTime,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer size) {
AuditLogQuery query = AuditLogQuery.builder()
.riskLevel(riskLevel)
.startTime(startTime > 0 ? LocalDateTime.ofInstant(
Instant.ofEpochMilli(startTime), ZoneId.systemDefault()) : null)
.page(page)
.size(size)
.build();
PageResult<AuditLogWithDetails> result = auditLogService.getAuditLogsWithDetails(query);
return Result.success(result);
}
/**
* 查询高风险操作(安全监控专用)
*/
@GetMapping("/high-risk")
public Result<List<AuditLog>> getHighRiskOperations(
@RequestParam(defaultValue = "86400000") Long timeWindow) { // 默认24小时
LocalDateTime startTime = LocalDateTime.now().minusMillis(timeWindow);
List<AuditLog> highRiskLogs = auditLogService.getHighRiskAuditLogs(startTime);
return Result.success(highRiskLogs);
}
/**
* 获取操作详情(包含前后状态对比)
*/
@GetMapping("/logs/{auditLogId}/details")
public Result<AuditLogWithDetails> getAuditLogDetails(@PathVariable Long auditLogId) {
AuditLogWithDetails details = auditLogService.getAuditLogWithDetails(auditLogId);
if (details == null) {
return Result.error("审计日志不存在");
}
return Result.success(details);
}
}
六、完整审计体系实现
6.1 审计日志实体增强
/**
* 审计日志详情实体
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuditLogDetail {
private Long id;
private String beforeState; // 操作前状态(JSON)
private String afterState; // 操作后状态(JSON)
private String additionalInfo; // 额外信息(JSON)
private LocalDateTime createTime;
}
/**
* 审计日志带详情的响应DTO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuditLogWithDetails {
private AuditLog auditLog;
private AuditLogDetail detail;
/**
* 获取完整的操作详情
*/
public Map<String, Object> getCompleteDetails() {
Map<String, Object> result = new HashMap<>();
// 如果有详情表数据,优先使用
if (detail != null) {
if (detail.getAfterState() != null) {
result.put("afterState", JsonUtils.parseMap(detail.getAfterState()));
}
if (detail.getBeforeState() != null) {
result.put("beforeState", JsonUtils.parseMap(detail.getBeforeState()));
}
if (detail.getAdditionalInfo() != null) {
result.put("additionalInfo", JsonUtils.parseMap(detail.getAdditionalInfo()));
}
} else if (auditLog.getOperationDetails() != null) {
// 使用主表的简单详情
result.put("details", JsonUtils.parseMap(auditLog.getOperationDetails()));
}
return result;
}
}
6.2 风险等级评估策略
/**
* 风险等级评估器
*/
@Component
public class RiskLevelEvaluator {
private static final Set<String> CRITICAL_PERMISSIONS = Set.of(
"USER_DELETE", "ROLE_DELETE", "PERMISSION_DELETE",
"FINANCE_TRANSFER", "SYSTEM_SHUTDOWN"
);
private static final Set<String> HIGH_RISK_OPERATIONS = Set.of(
"DELETE", "REVOKE", "DISABLE", "RESET_PASSWORD"
);
/**
* 评估操作的风险等级
*/
public String evaluateRiskLevel(String operationType, String targetType,
String permissionCode) {
// 关键权限操作
if (permissionCode != null && CRITICAL_PERMISSIONS.contains(permissionCode)) {
return "CRITICAL";
}
// 高风险操作类型
if (HIGH_RISK_OPERATIONS.stream().anyMatch(operationType::contains)) {
return "HIGH";
}
// 默认中等风险
return "MEDIUM";
}
}
七、测试用例验证
7.1 完整RBAC3场景测试
@SpringBootTest
class Rbac3PermissionServiceTest {
@Autowired
private Rbac3PermissionService permissionService;
@Test
void testRbac3CompleteScenario() {
// 场景:用户拥有经理角色,经理继承员工角色
// 员工有 SALARY_VIEW 权限,经理有 LEAVE_APPROVE 权限
// 会计和出纳互斥,总监有基数限制
// 1. 测试继承权限
assertTrue(permissionService.hasPermission(1001L, "SALARY_VIEW", createContext()));
assertTrue(permissionService.hasPermission(1001L, "LEAVE_APPROVE", createContext()));
// 2. 测试互斥约束
ConstraintViolationException exception = assertThrows(
ConstraintViolationException.class,
() -> permissionService.assignRoleToUser(1001L, 5L, 1L, createContext()) // 分配出纳角色
);
assertEquals("MUTUAL_EXCLUSION", exception.getConstraintType());
// 3. 测试基数约束
// 分配总监角色给第3个用户(限制为2人)
exception = assertThrows(
ConstraintViolationException.class,
() -> permissionService.assignRoleToUser(1003L, 3L, 1L, createContext())
);
assertEquals("CARDINALITY", exception.getConstraintType());
// 4. 测试先决条件
// 直接分配总监角色给无经理角色的用户
exception = assertThrows(
ConstraintViolationException.class,
() -> permissionService.assignRoleToUser(1004L, 3L, 1L, createContext())
);
assertEquals("PREREQUISITE", exception.getConstraintType());
}
@Test
void testAuditLogRecording() {
// 执行权限检查
permissionService.hasPermission(1001L, "SALARY_VIEW", createContext());
// 查询审计日志
List<AuditLog> logs = auditLogService.getRecentAuditLogs(1001L, "PERMISSION");
assertEquals(1, logs.size());
assertEquals("ACCESS_SUCCESS", logs.get(0).getOperationType());
assertEquals("MEDIUM", logs.get(0).getRiskLevel());
}
private RequestContext createContext() {
return RequestContext.builder()
.ipAddress("192.168.1.100")
.userAgent("Test-Agent")
.sessionId("test-session")
.build();
}
}
7.2 审计日志完整性测试
@Test
void testAuditLogWithDetails() {
// 执行复杂操作(详情超过1000字符)
Map<String, Object> complexDetails = new HashMap<>();
for (int i = 0; i < 100; i++) {
complexDetails.put("field" + i, "value" + i + "abcdefghijklmnopqrstuvwxyz");
}
auditLogService.logOperation(
"COMPLEX_OPERATION",
"TEST",
1L,
1L,
"192.168.1.1",
"Test-Agent",
"test-session",
"HIGH",
complexDetails
);
// 查询审计日志详情
List<AuditLog> logs = auditLogMapper.selectRecentLogs(1, 1);
AuditLogWithDetails details = auditLogService.getAuditLogWithDetails(logs.get(0).getId());
assertNotNull(details.getDetail());
assertTrue(details.getCompleteDetails().containsKey("afterState"));
}
八、性能优化建议
8.1 缓存策略
/**
* RBAC3缓存策略
*/
@Component
public class Rbac3CacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 用户完整角色集缓存(10分钟)
private static final String USER_ROLES_KEY = "rbac3:user:roles:%d";
// 角色权限缓存(30分钟)
private static final String ROLE_PERMISSIONS_KEY = "rbac3:role:permissions:%d";
// 约束规则缓存(1小时)
private static final String CONSTRAINTS_KEY = "rbac3:constraints";
public Set<Long> getCachedUserRoles(Long userId) {
String key = String.format(USER_ROLES_KEY, userId);
return (Set<Long>) redisTemplate.opsForValue().get(key);
}
public void cacheUserRoles(Long userId, Set<Long> roles) {
String key = String.format(USER_ROLES_KEY, userId);
redisTemplate.opsForValue().set(key, roles, 10, TimeUnit.MINUTES);
}
// 缓存失效策略
public void evictUserRoles(Long userId) {
String key = String.format(USER_ROLES_KEY, userId);
redisTemplate.delete(key);
}
}
8.2 数据库优化
- 索引优化:为频繁查询的字段添加复合索引
- 分区表:审计日志按时间分区
- 读写分离:权限查询走从库,写操作走主库
8.3 异步审计
/**
* 异步审计日志记录
*/
@Async
public void asyncLogOperation(AuditLog auditLog) {
auditLogMapper.insert(auditLog);
}
总结
RBAC3 模型通过整合 RBAC1 的角色继承和 RBAC2 的约束机制,提供了企业级权限系统的完整解决方案。结合完善的审计功能,能够满足最严格的合规和安全要求。
核心优势
- 继承灵活性:减少权限重复配置,支持复杂组织架构
- 约束安全性:确保职责分离,防止权限滥用
- 完整可追溯:所有操作都有详细审计记录
- 风险可控:基于风险等级的监控和告警
实施要点
- 渐进式实施:从 RBAC0 开始,逐步增加继承和约束
- 性能监控:重点关注权限验证的响应时间
- 审计合规:确保审计日志满足法规要求
- 用户体验:提供清晰的约束违规提示
RBAC3 是构建安全、可靠、可维护的权限系统的最佳实践,特别适用于对安全性要求极高的企业级应用。
1867

被折叠的 条评论
为什么被折叠?



