目录
- RBAC1 模型核心概念
- 数据库表结构设计(含审计)
- 工作流程详解
- Java 实现示例(含详细中文注释)
- API 使用示例
- 审计功能实现
- 测试用例验证
一、RBAC1 模型核心概念
1.1 什么是 RBAC1?
RBAC1(Role-Based Access Control Level 1)在 RBAC0 的基础上增加了角色继承特性,允许角色之间形成层次结构:
- 子角色自动继承父角色的所有权限
- 支持多级继承(树形或森林结构)
- 解决了权限重复分配的问题
1.2 核心增强点
| 特性 | RBAC0 | RBAC1 |
|---|---|---|
| 角色关系 | 平面结构 | 层次结构 |
| 权限继承 | 无 | 子角色继承父角色权限 |
| 管理复杂度 | 高(重复分配) | 低(一次定义,多处继承) |
1.3 形象类比
公司组织架构升级版:
- 员工角色:拥有基础权限(查看工资单、提交请假申请)
- 经理角色:继承员工所有权限 + 额外权限(审批请假、查看团队工资)
- 总监角色:继承经理所有权限 + 额外权限(团队管理、预算审批)
当给经理分配权限时,只需分配"审批请假"权限,自动获得员工的所有基础权限
1.4 继承规则
- 传递性:如果 A 继承 B,B 继承 C,则 A 继承 C
- 累积性:子角色拥有自身权限 + 所有祖先角色权限
- 无环性:不能形成循环继承(A→B→A)
二、数据库表结构设计(含审计)
2.1 表结构总览
| 表名 | 说明 | 关系 |
|---|---|---|
sys_user | 用户表 | - |
sys_role | 角色表 | - |
sys_role_hierarchy | 角色继承关系表 | 父角色 ↔ 子角色 |
sys_permission | 权限表 | - |
sys_user_role | 用户-角色关联表 | 用户 ↔ 角色 |
sys_role_permission | 角色-权限关联表 | 角色 ↔ 权限 |
sys_audit_log | 审计日志表 | 记录所有权限变更 |
2.2 新增/修改的表结构
2.2.1 角色表 (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-根角色,1-一级子角色...)',
`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.2 角色继承关系表 (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.3 审计日志表 (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',
`target_type` VARCHAR(20) NOT NULL COMMENT '目标类型:USER/ROLE/PERMISSION/HIERARCHY',
`target_id` BIGINT NOT NULL COMMENT '目标ID',
`operator_id` BIGINT NOT NULL COMMENT '操作人ID',
`operator_name` VARCHAR(50) NOT NULL COMMENT '操作人姓名',
`operation_details` JSON DEFAULT NULL COMMENT '操作详情(JSON格式)',
`ip_address` VARCHAR(45) DEFAULT NULL COMMENT '操作IP地址',
`user_agent` VARCHAR(500) DEFAULT NULL COMMENT '用户代理',
`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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限系统审计日志表';
2.3 示例数据
角色数据
-- 基础角色
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);
-- 总监继承经理权限(间接继承员工权限)
INSERT INTO sys_role_hierarchy (parent_role_id, child_role_id, hierarchy_level)
VALUES (2, 3, 1);
权限数据
-- 员工权限
INSERT INTO sys_permission (permission_code, permission_name, resource_type, resource_path)
VALUES
('SALARY_VIEW', '查看工资', 'API', '/api/salary/view'),
('LEAVE_APPLY', '申请请假', 'API', '/api/leave/apply');
-- 经理额外权限
INSERT INTO sys_permission (permission_code, permission_name, resource_type, resource_path)
VALUES
('LEAVE_APPROVE', '审批请假', 'API', '/api/leave/approve'),
('TEAM_SALARY_VIEW', '查看团队工资', 'API', '/api/salary/team');
-- 总监额外权限
INSERT INTO sys_permission (permission_code, permission_name, resource_type, resource_path)
VALUES
('TEAM_MANAGE', '团队管理', 'API', '/api/team/manage'),
('BUDGET_APPROVE', '预算审批', 'API', '/api/budget/approve');
角色-权限关联
-- 员工角色拥有基础权限
INSERT INTO sys_role_permission (role_id, permission_id)
VALUES (1, 1), (1, 2);
-- 经理角色拥有额外权限(自动继承员工权限)
INSERT INTO sys_role_permission (role_id, permission_id)
VALUES (2, 3), (2, 4);
-- 总监角色拥有额外权限(自动继承经理和员工权限)
INSERT INTO sys_role_permission (role_id, permission_id)
VALUES (3, 5), (3, 6);
三、RBAC1 工作流程详解
3.1 角色继承构建流程
3.2 权限验证流程(关键增强)
3.3 核心验证逻辑(与RBAC0的区别)
- 获取当前用户直接关联的角色
- 递归查询这些角色的所有祖先角色
- 合并直接角色 + 所有祖先角色
- 查询所有角色关联的权限
- 检查目标权限是否在权限列表中
3.4 继承关系查询策略
- 方案1:递归查询(简单但性能较差)
- 方案2:路径枚举(存储完整路径,查询快)
- 方案3:闭包表(预计算所有继承关系,推荐)
本方案采用闭包表策略:
- 在
sys_role_hierarchy表中预存储所有直接和间接继承关系 - 通过
hierarchy_level字段区分直接继承(1)和间接继承(>1)
四、Java 实现示例(含详细中文注释)
4.1 新增实体类
角色继承关系实体
/**
* 角色继承关系实体类
* 对应数据库表:sys_role_hierarchy
* 用于表示角色之间的继承关系
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleHierarchy {
/**
* 关系ID,主键
*/
private Long id;
/**
* 父角色ID(被继承的角色)
* 例如:员工角色ID
*/
private Long parentRoleId;
/**
* 子角色ID(继承的角色)
* 例如:经理角色ID
*/
private Long childRoleId;
/**
* 继承层级:
* 1 - 直接子角色(经理直接继承员工)
* 2 - 间接子角色(总监间接继承员工)
* 数值越大,继承距离越远
*/
private Integer hierarchyLevel;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
审计日志实体
/**
* 审计日志实体类
* 对应数据库表:sys_audit_log
* 记录权限系统的所有关键操作
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuditLog {
/**
* 审计日志ID,主键
*/
private Long id;
/**
* 操作类型:
* CREATE - 创建
* UPDATE - 更新
* DELETE - 删除
* ASSIGN - 分配(如分配角色给用户)
* REVOKE - 撤销(如撤销角色)
*/
private String operationType;
/**
* 目标类型:
* USER - 用户
* ROLE - 角色
* PERMISSION - 权限
* HIERARCHY - 继承关系
*/
private String targetType;
/**
* 目标ID(被操作对象的ID)
*/
private Long targetId;
/**
* 操作人ID
*/
private Long operatorId;
/**
* 操作人姓名(冗余存储,便于审计查询)
*/
private String operatorName;
/**
* 操作详情(JSON格式)
* 包含操作前后的数据对比等详细信息
*/
private String operationDetails;
/**
* 操作IP地址
*/
private String ipAddress;
/**
* 用户代理(浏览器信息)
*/
private String userAgent;
/**
* 操作时间
*/
private LocalDateTime createTime;
}
4.2 增强的Mapper接口
角色继承关系Mapper
/**
* 角色继承关系Mapper
* 负责角色继承关系的数据库操作
*/
@Mapper
public interface RoleHierarchyMapper {
/**
* 插入角色继承关系
* @param hierarchy 角色继承关系对象
* @return 影响行数
*/
int insert(RoleHierarchy hierarchy);
/**
* 根据子角色ID查询所有祖先角色ID(包括间接祖先)
* 这是RBAC1的核心查询方法
*
* 例如:查询总监的所有祖先角色,返回[经理, 员工]
*
* @param childRoleId 子角色ID
* @return 祖先角色ID列表
*/
List<Long> selectAllAncestorRoleIdsByChildId(Long childRoleId);
/**
* 根据父角色ID查询所有后代角色ID
* 用于级联操作(如删除角色时检查是否有子角色)
*
* @param parentRoleId 父角色ID
* @return 后代角色ID列表
*/
List<Long> selectAllDescendantRoleIdsByParentId(Long parentRoleId);
/**
* 检查是否存在循环继承
* 在添加继承关系前调用,防止A→B→A的情况
*
* @param parentRoleId 潜在父角色ID
* @param childRoleId 潜在子角色ID
* @return true-存在循环,false-无循环
*/
boolean existsCircularReference(@Param("parentRoleId") Long parentRoleId,
@Param("childRoleId") Long childRoleId);
/**
* 删除角色的所有继承关系(级联删除)
* @param roleId 角色ID
* @return 影响行数
*/
int deleteByRoleId(Long roleId);
}
审计日志Mapper
/**
* 审计日志Mapper
* 负责审计日志的数据库操作
*/
@Mapper
public interface AuditLogMapper {
/**
* 插入审计日志
* @param auditLog 审计日志对象
* @return 影响行数
*/
int insert(AuditLog auditLog);
/**
* 根据条件查询审计日志(分页)
* @param query 审计日志查询条件
* @return 审计日志列表
*/
List<AuditLog> selectByCondition(AuditLogQuery query);
/**
* 根据目标类型和ID查询相关审计日志
* 用于查看某个对象的操作历史
*
* @param targetType 目标类型
* @param targetId 目标ID
* @return 审计日志列表
*/
List<AuditLog> selectByTarget(String targetType, Long targetId);
}
4.3 RBAC1权限服务核心实现
/**
* RBAC1权限服务实现类(继承RBAC0,增强角色继承功能)
* 负责权限的核心业务逻辑处理,支持角色继承
*/
@Service
@Slf4j
public class Rbac1PermissionService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private RoleHierarchyMapper roleHierarchyMapper;
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private AuditLogService auditLogService;
/**
* 检查用户是否拥有指定权限(支持角色继承)
* RBAC1的核心验证方法,相比RBAC0增加了继承关系处理
*
* 验证流程:
* 1. 根据用户ID获取用户直接关联的角色ID列表
* 2. 对每个直接角色,查询其所有祖先角色ID
* 3. 合并直接角色ID + 所有祖先角色ID,去重
* 4. 查询所有角色(直接+继承)关联的权限ID列表
* 5. 检查目标权限编码是否在权限列表中
*
* @param userId 用户ID
* @param permissionCode 目标权限编码(如:LEAVE_APPROVE)
* @return true-拥有权限,false-无权限
*/
public boolean hasPermission(Long userId, String permissionCode) {
log.info("开始检查用户权限(RBAC1): userId={}, permissionCode={}", userId, permissionCode);
try {
// 步骤1: 获取用户直接关联的角色ID列表
List<Long> directRoleIds = userMapper.selectRoleIdsByUserId(userId);
log.debug("用户直接关联的角色ID列表: {}", directRoleIds);
// 如果用户没有任何直接角色,直接返回无权限
if (CollectionUtils.isEmpty(directRoleIds)) {
log.warn("用户没有分配任何直接角色,userId={}", userId);
return false;
}
// 步骤2: 获取所有祖先角色ID(包括间接祖先)
Set<Long> allRoleIds = new HashSet<>(directRoleIds); // 包含直接角色
for (Long directRoleId : directRoleIds) {
List<Long> ancestorRoleIds = roleHierarchyMapper.selectAllAncestorRoleIdsByChildId(directRoleId);
allRoleIds.addAll(ancestorRoleIds);
log.debug("角色 {} 的祖先角色ID列表: {}", directRoleId, ancestorRoleIds);
}
log.debug("用户拥有的完整角色ID集合(直接+继承): {}", allRoleIds);
// 步骤3: 获取所有角色关联的权限ID列表
List<Long> permissionIds = roleMapper.selectPermissionIdsByRoleIds(new ArrayList<>(allRoleIds));
log.debug("角色关联的权限ID列表: {}", permissionIds);
// 如果没有任何权限,直接返回无权限
if (CollectionUtils.isEmpty(permissionIds)) {
log.warn("角色没有分配任何权限,roleIds={}", allRoleIds);
return false;
}
// 步骤4: 获取权限编码列表并检查
List<Permission> permissions = permissionMapper.selectByIds(permissionIds);
Set<String> permissionCodes = permissions.stream()
.map(Permission::getPermissionCode)
.collect(Collectors.toSet());
log.debug("用户拥有的权限编码列表: {}", permissionCodes);
boolean hasPermission = permissionCodes.contains(permissionCode);
log.info("RBAC1权限检查结果: userId={}, permissionCode={}, result={}",
userId, permissionCode, hasPermission);
return hasPermission;
} catch (Exception e) {
log.error("RBAC1权限检查发生异常", e);
return false;
}
}
/**
* 获取用户拥有的所有权限编码列表(支持角色继承)
*
* @param userId 用户ID
* @return 权限编码集合
*/
public Set<String> getUserPermissionCodes(Long userId) {
log.info("获取用户权限列表(RBAC1): userId={}", userId);
try {
// 获取直接角色ID
List<Long> directRoleIds = userMapper.selectRoleIdsByUserId(userId);
if (CollectionUtils.isEmpty(directRoleIds)) {
return Collections.emptySet();
}
// 获取所有角色ID(直接+祖先)
Set<Long> allRoleIds = new HashSet<>(directRoleIds);
for (Long directRoleId : directRoleIds) {
List<Long> ancestorRoleIds = roleHierarchyMapper.selectAllAncestorRoleIdsByChildId(directRoleId);
allRoleIds.addAll(ancestorRoleIds);
}
// 获取权限
List<Long> permissionIds = roleMapper.selectPermissionIdsByRoleIds(new ArrayList<>(allRoleIds));
if (CollectionUtils.isEmpty(permissionIds)) {
return Collections.emptySet();
}
List<Permission> permissions = permissionMapper.selectByIds(permissionIds);
return permissions.stream()
.map(Permission::getPermissionCode)
.collect(Collectors.toSet());
} catch (Exception e) {
log.error("获取用户权限列表发生异常", e);
return Collections.emptySet();
}
}
/**
* 设置角色继承关系
* 建立父角色和子角色之间的继承关系
*
* @param parentRoleId 父角色ID
* @param childRoleId 子角色ID
* @param operatorId 操作人ID
* @param request 审计信息
* @throws BusinessException 业务异常
*/
@Transactional(rollbackFor = Exception.class)
public void setRoleInheritance(Long parentRoleId, Long childRoleId,
Long operatorId, AuditRequest request) {
log.info("设置角色继承关系: parentRoleId={}, childRoleId={}, operatorId={}",
parentRoleId, childRoleId, operatorId);
// 1. 参数校验
if (parentRoleId.equals(childRoleId)) {
throw new BusinessException("不能设置角色自继承");
}
// 2. 检查循环继承
if (roleHierarchyMapper.existsCircularReference(parentRoleId, childRoleId)) {
throw new BusinessException("存在循环继承风险,无法设置继承关系");
}
// 3. 检查是否已存在继承关系
RoleHierarchy existing = roleHierarchyMapper.selectByParentAndChild(parentRoleId, childRoleId);
if (existing != null) {
throw new BusinessException("继承关系已存在");
}
// 4. 创建继承关系
RoleHierarchy hierarchy = RoleHierarchy.builder()
.parentRoleId(parentRoleId)
.childRoleId(childRoleId)
.hierarchyLevel(1) // 直接继承
.build();
roleHierarchyMapper.insert(hierarchy);
// 5. 递归更新间接继承关系(闭包表维护)
updateIndirectInheritance(parentRoleId, childRoleId);
// 6. 记录审计日志
auditLogService.logRoleInheritance(
operatorId,
parentRoleId,
childRoleId,
request.getIpAddress(),
request.getUserAgent()
);
log.info("角色继承关系设置成功");
}
/**
* 递归更新间接继承关系
* 当添加新的直接继承关系时,需要更新所有相关的间接继承关系
*
* 例如:添加 A→B,而 B→C 已存在,则需要添加 A→C(间接)
*
* @param parentRoleId 新的父角色ID
* @param childRoleId 新的子角色ID
*/
private void updateIndirectInheritance(Long parentRoleId, Long childRoleId) {
// 1. 获取子角色的所有后代角色(B的所有后代:C, D...)
List<Long> descendants = roleHierarchyMapper.selectAllDescendantRoleIdsByParentId(childRoleId);
// 2. 为每个后代角色添加间接继承关系(A→C, A→D...)
for (Long descendantId : descendants) {
// 计算继承层级:A→B(1) + B→C(1) = A→C(2)
Integer level = getInheritanceLevel(parentRoleId, descendantId);
if (level != null) {
RoleHierarchy indirect = RoleHierarchy.builder()
.parentRoleId(parentRoleId)
.childRoleId(descendantId)
.hierarchyLevel(level)
.build();
// 忽略重复插入异常
try {
roleHierarchyMapper.insert(indirect);
} catch (Exception e) {
log.debug("间接继承关系已存在,跳过: {}→{}", parentRoleId, descendantId);
}
}
}
// 3. 获取父角色的所有祖先角色(A的所有祖先:X, Y...)
List<Long> ancestors = roleHierarchyMapper.selectAllAncestorRoleIdsByChildId(parentRoleId);
// 4. 为每个祖先角色添加到子角色及其后代的间接继承关系
for (Long ancestorId : ancestors) {
// 祖先→子角色
Integer level1 = getInheritanceLevel(ancestorId, childRoleId);
if (level1 != null) {
RoleHierarchy indirect1 = RoleHierarchy.builder()
.parentRoleId(ancestorId)
.childRoleId(childRoleId)
.hierarchyLevel(level1)
.build();
try {
roleHierarchyMapper.insert(indirect1);
} catch (Exception e) {
log.debug("间接继承关系已存在,跳过: {}→{}", ancestorId, childRoleId);
}
}
// 祖先→后代角色
for (Long descendantId : descendants) {
Integer level2 = getInheritanceLevel(ancestorId, descendantId);
if (level2 != null) {
RoleHierarchy indirect2 = RoleHierarchy.builder()
.parentRoleId(ancestorId)
.childRoleId(descendantId)
.hierarchyLevel(level2)
.build();
try {
roleHierarchyMapper.insert(indirect2);
} catch (Exception e) {
log.debug("间接继承关系已存在,跳过: {}→{}", ancestorId, descendantId);
}
}
}
}
}
/**
* 计算两个角色之间的继承层级
* 通过现有继承关系计算最短路径
*/
private Integer getInheritanceLevel(Long ancestorId, Long descendantId) {
// 简化实现:通过BFS计算最短路径
// 实际项目中可以优化为存储路径或使用图算法
Queue<RolePath> queue = new LinkedList<>();
Set<Long> visited = new HashSet<>();
queue.offer(new RolePath(descendantId, 0));
visited.add(descendantId);
while (!queue.isEmpty()) {
RolePath current = queue.poll();
if (current.roleId.equals(ancestorId)) {
return current.level;
}
// 获取当前角色的直接父角色
List<Long> directParents = roleHierarchyMapper.selectDirectParentRoleIds(current.roleId);
for (Long parentId : directParents) {
if (!visited.contains(parentId)) {
visited.add(parentId);
queue.offer(new RolePath(parentId, current.level + 1));
}
}
}
return null; // 无继承关系
}
/**
* 内部类:用于BFS路径计算
*/
@Data
@AllArgsConstructor
private static class RolePath {
private Long roleId;
private Integer level;
}
}
4.4 审计服务实现
/**
* 审计日志服务
* 负责记录权限系统的所有关键操作
*/
@Service
@Slf4j
public class AuditLogService {
@Autowired
private AuditLogMapper auditLogMapper;
@Autowired
private UserMapper userMapper;
/**
* 记录角色继承关系设置的审计日志
*
* @param operatorId 操作人ID
* @param parentRoleId 父角色ID
* @param childRoleId 子角色ID
* @param ipAddress 操作IP
* @param userAgent 用户代理
*/
public void logRoleInheritance(Long operatorId, Long parentRoleId,
Long childRoleId, String ipAddress, String userAgent) {
try {
// 获取操作人信息
User operator = userMapper.selectById(operatorId);
String operatorName = operator != null ? operator.getRealName() : "未知用户";
// 构建操作详情
Map<String, Object> details = new HashMap<>();
details.put("parentRoleId", parentRoleId);
details.put("childRoleId", childRoleId);
details.put("operation", "SET_INHERITANCE");
// 创建审计日志
AuditLog auditLog = AuditLog.builder()
.operationType("ASSIGN")
.targetType("HIERARCHY")
.targetId(parentRoleId) // 以父角色作为目标
.operatorId(operatorId)
.operatorName(operatorName)
.operationDetails(JsonUtils.toJson(details))
.ipAddress(ipAddress)
.userAgent(userAgent)
.build();
auditLogMapper.insert(auditLog);
log.info("审计日志记录成功: operator={}, operation=SET_INHERITANCE", operatorName);
} catch (Exception e) {
log.error("记录审计日志失败", e);
// 审计日志失败不应影响主业务流程
}
}
/**
* 记录通用的权限操作审计日志
*/
public void logPermissionOperation(String operationType, String targetType,
Long targetId, Long operatorId,
String ipAddress, String userAgent,
Object operationDetails) {
try {
User operator = userMapper.selectById(operatorId);
String operatorName = operator != null ? operator.getRealName() : "未知用户";
AuditLog auditLog = AuditLog.builder()
.operationType(operationType)
.targetType(targetType)
.targetId(targetId)
.operatorId(operatorId)
.operatorName(operatorName)
.operationDetails(operationDetails != null ? JsonUtils.toJson(operationDetails) : null)
.ipAddress(ipAddress)
.userAgent(userAgent)
.build();
auditLogMapper.insert(auditLog);
} catch (Exception e) {
log.error("记录审计日志失败", e);
}
}
}
五、API 使用示例
5.1 角色继承管理API
/**
* 角色管理Controller(支持继承)
*/
@RestController
@RequestMapping("/api/role")
public class RoleController {
@Autowired
private Rbac1PermissionService permissionService;
/**
* 设置角色继承关系
* 建立父角色和子角色的继承关系
*
* 请求示例:
* POST /api/role/inheritance
* {
* "parentRoleId": 1,
* "childRoleId": 2
* }
*
* 说明:设置角色2(经理)继承角色1(员工)的权限
*/
@PostMapping("/inheritance")
public Result<Void> setRoleInheritance(@RequestBody @Valid SetInheritanceRequest request,
HttpServletRequest httpRequest) {
// 获取当前操作人
Long currentUserId = getCurrentUserId(httpRequest);
// 构建审计信息
AuditRequest auditRequest = AuditRequest.builder()
.ipAddress(getClientIpAddress(httpRequest))
.userAgent(httpRequest.getHeader("User-Agent"))
.build();
// 设置继承关系
permissionService.setRoleInheritance(
request.getParentRoleId(),
request.getChildRoleId(),
currentUserId,
auditRequest
);
return Result.success();
}
/**
* 获取角色的完整权限树(用于展示)
* 显示角色自身的权限 + 继承的权限
*/
@GetMapping("/{roleId}/permission-tree")
public Result<RolePermissionTree> getRolePermissionTree(@PathVariable Long roleId) {
RolePermissionTree tree = buildPermissionTree(roleId);
return Result.success(tree);
}
/**
* 构建角色权限树
* 包含直接权限和继承权限的详细信息
*/
private RolePermissionTree buildPermissionTree(Long roleId) {
// 获取角色基本信息
Role role = roleMapper.selectById(roleId);
// 获取直接权限
List<Permission> directPermissions = getDirectPermissions(roleId);
// 获取继承权限
List<RoleInheritanceInfo> inheritanceChain = getInheritanceChain(roleId);
return RolePermissionTree.builder()
.role(role)
.directPermissions(directPermissions)
.inheritanceChain(inheritanceChain)
.build();
}
}
5.2 请求DTO定义
/**
* 设置角色继承关系请求DTO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SetInheritanceRequest {
/**
* 父角色ID(被继承的角色)
* 例如:员工角色ID
*/
@NotNull(message = "父角色ID不能为空")
private Long parentRoleId;
/**
* 子角色ID(继承的角色)
* 例如:经理角色ID
*/
@NotNull(message = "子角色ID不能为空")
private Long childRoleId;
}
/**
* 审计请求信息
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuditRequest {
/**
* 操作IP地址
*/
private String ipAddress;
/**
* 用户代理信息
*/
private String userAgent;
}
/**
* 角色权限树响应DTO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RolePermissionTree {
/**
* 当前角色信息
*/
private Role role;
/**
* 直接权限列表(角色自身拥有的权限)
*/
private List<Permission> directPermissions;
/**
* 继承链信息(从哪些角色继承了什么权限)
*/
private List<RoleInheritanceInfo> inheritanceChain;
}
/**
* 角色继承信息
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleInheritanceInfo {
/**
* 祖先角色信息
*/
private Role ancestorRole;
/**
* 从该祖先角色继承的权限列表
*/
private List<Permission> inheritedPermissions;
/**
* 继承层级(1-直接父角色,2-祖父角色...)
*/
private Integer inheritanceLevel;
}
六、审计功能详细说明
6.1 审计覆盖范围
| 操作类型 | 审计内容 | 审计级别 |
|---|---|---|
| 角色管理 | 创建/更新/删除角色 | 高 |
| 权限分配 | 角色分配权限、用户分配角色 | 高 |
| 继承关系 | 设置/删除角色继承 | 最高 |
| 权限验证 | 敏感权限访问(可选) | 中 |
6.2 审计日志查询API
/**
* 审计日志查询Controller
*/
@RestController
@RequestMapping("/api/audit")
public class AuditLogController {
@Autowired
private AuditLogService auditLogService;
/**
* 分页查询审计日志
* 支持多种查询条件
*/
@GetMapping("/logs")
public Result<PageResult<AuditLog>> getAuditLogs(AuditLogQuery query) {
PageResult<AuditLog> result = auditLogService.getAuditLogs(query);
return Result.success(result);
}
/**
* 查询特定对象的操作历史
* 例如:查看某个角色的所有操作记录
*/
@GetMapping("/target/{targetType}/{targetId}")
public Result<List<AuditLog>> getTargetAuditLogs(@PathVariable String targetType,
@PathVariable Long targetId) {
List<AuditLog> logs = auditLogService.getTargetAuditLogs(targetType, targetId);
return Result.success(logs);
}
}
6.3 审计日志示例
角色继承设置审计日志:
{
"id": 1001,
"operationType": "ASSIGN",
"targetType": "HIERARCHY",
"targetId": 1,
"operatorId": 100,
"operatorName": "系统管理员",
"operationDetails": {
"parentRoleId": 1,
"childRoleId": 2,
"operation": "SET_INHERITANCE"
},
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"createTime": "2024-01-15T10:30:00"
}
权限分配审计日志:
{
"id": 1002,
"operationType": "ASSIGN",
"targetType": "PERMISSION",
"targetId": 5,
"operatorId": 100,
"operatorName": "系统管理员",
"operationDetails": {
"roleId": 2,
"permissionId": 5,
"permissionCode": "TEAM_MANAGE",
"operation": "ASSIGN_PERMISSION"
},
"ipAddress": "192.168.1.100",
"createTime": "2024-01-15T10:35:00"
}
七、测试用例验证
7.1 角色继承测试
/**
* RBAC1权限服务测试类
*/
@SpringBootTest
class Rbac1PermissionServiceTest {
@Autowired
private Rbac1PermissionService permissionService;
@Test
void testHasPermission_WithRoleInheritance_ShouldIncludeInheritedPermissions() {
// 准备数据:
// - 用户ID=1 分配了经理角色(ID=2)
// - 经理角色继承员工角色(ID=1)
// - 员工角色拥有 SALARY_VIEW 权限
// - 经理角色拥有 LEAVE_APPROVE 权限
// 测试1:经理应该拥有员工的权限
boolean canViewSalary = permissionService.hasPermission(1L, "SALARY_VIEW");
assertTrue(canViewSalary, "经理应该继承员工的查看工资权限");
// 测试2:经理应该拥有自己的权限
boolean canApproveLeave = permissionService.hasPermission(1L, "LEAVE_APPROVE");
assertTrue(canApproveLeave, "经理应该拥有审批请假权限");
// 测试3:经理不应该拥有总监的权限
boolean canManageTeam = permissionService.hasPermission(1L, "TEAM_MANAGE");
assertFalse(canManageTeam, "经理不应该拥有团队管理权限");
}
@Test
void testGetUserPermissionCodes_ShouldIncludeAllInheritedPermissions() {
Set<String> permissions = permissionService.getUserPermissionCodes(1L);
// 应该包含员工权限 + 经理权限
assertTrue(permissions.contains("SALARY_VIEW"), "应该包含继承的员工权限");
assertTrue(permissions.contains("LEAVE_APPLY"), "应该包含继承的员工权限");
assertTrue(permissions.contains("LEAVE_APPROVE"), "应该包含经理自身权限");
assertTrue(permissions.contains("TEAM_SALARY_VIEW"), "应该包含经理自身权限");
// 不应该包含总监权限
assertFalse(permissions.contains("TEAM_MANAGE"), "不应该包含总监权限");
assertFalse(permissions.contains("BUDGET_APPROVE"), "不应该包含总监权限");
assertEquals(4, permissions.size(), "应该有4个权限");
}
@Test
void testSetRoleInheritance_WithCircularReference_ShouldThrowException() {
// 准备数据:A→B, B→C 已存在
// 尝试设置 C→A(形成循环)
assertThrows(BusinessException.class, () -> {
permissionService.setRoleInheritance(3L, 1L, 100L, createAuditRequest());
});
}
}
7.2 审计功能测试
@Test
void testAuditLog_WhenSetRoleInheritance_ShouldRecordAuditLog() {
// 执行设置继承关系操作
permissionService.setRoleInheritance(1L, 2L, 100L, createAuditRequest());
// 查询审计日志
List<AuditLog> logs = auditLogService.getTargetAuditLogs("HIERARCHY", 1L);
// 验证审计日志
assertEquals(1, logs.size(), "应该记录1条审计日志");
AuditLog log = logs.get(0);
assertEquals("ASSIGN", log.getOperationType());
assertEquals("HIERARCHY", log.getTargetType());
assertEquals(100L, log.getOperatorId());
assertTrue(log.getOperationDetails().contains("parentRoleId"));
assertTrue(log.getOperationDetails().contains("childRoleId"));
}
总结
RBAC1 模型通过引入角色继承机制,大大简化了权限管理的复杂度。结合完善的审计功能,可以构建一个既灵活又安全的权限控制系统。
关键优势
- 减少权限重复分配:通过继承避免重复配置
- 层次化管理:符合组织架构的自然层次
- 完整审计追踪:所有关键操作都有迹可循
- 安全防护:防止循环继承等异常情况
实施建议
- 继承层级控制:建议不超过5级,避免过度复杂
- 审计日志归档:定期归档历史审计日志,保证查询性能
- 缓存优化:对角色继承关系进行缓存,避免频繁数据库查询
- 权限变更通知:重要权限变更时发送通知给相关人员
这个实现方案适用于中大型企业应用,能够有效支撑复杂的组织架构和权限需求。
1548

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



