SpringBoot + 多角色权限叠加 + 权限继承:管理员 = 普通用户 + 审批权限,灵活组合

背景:权限管理的痛点

在企业级应用中,权限管理是一个绕不开的话题。随着业务的发展,权限体系变得越来越复杂:

  • 角色多样:普通用户、管理员、审批员、财务人员等
  • 权限叠加:一个用户可能同时拥有多个角色
  • 权限继承:高级角色应该自动继承低级角色的权限
  • 灵活配置:权限需要根据业务需求随时调整

传统的基于角色的访问控制(RBAC)模型已经无法满足复杂的业务场景。本文将介绍一种更灵活的权限管理方案:多角色权限叠加 + 权限继承

核心概念

1. 权限(Permission)

权限是对资源的访问控制,通常以资源:操作的形式表示,例如:

  • user:read - 读取用户信息
  • user:write - 写入用户信息
  • order:approve - 审批订单

2. 角色(Role)

角色是权限的集合,例如:

  • 普通用户user:read, order:create
  • 审批员order:approve, order:list
  • 管理员:应该拥有普通用户 + 审批员的所有权限

3. 角色继承(Role Inheritance)

高级角色可以继承低级角色的权限,例如:

  • 管理员继承审批员的权限
  • 审批员继承普通用户的权限

这样,管理员就自动拥有了普通用户和审批员的所有权限。

4. 多角色叠加(Multiple Role Overlay)

一个用户可以同时拥有多个角色,权限是所有角色权限的并集。

架构设计

系统架构

┌───────────────┐     ┌─────────────────┐     ┌─────────────────┐
│    客户端      │     │    服务端        │     │    数据库        │
└───────────────┘     └─────────────────┘     └─────────────────┘
        │                     │                     │
        │  1. 请求资源         │                     │
        │ ─────────────────>  │                     │
        │                     │  2. 验证用户身份      │
        │                     │                     │
        │                     │  3. 获取用户角色      │
        │                     │ ─────────────────>  │
        │                     │                     │
        │                     │  4. 计算用户权限      │
        │                     │  (继承 + 叠加)      │
        │                     │                     │
        │                     │  5. 验证权限         │
        │                     │                     │
        │  6. 返回结果         │                     │
        │ <─────────────────  │                     │

数据模型

1. 权限表(permission)

字段名数据类型描述
idbigint权限ID
codevarchar(64)权限编码
namevarchar(128)权限名称
descriptionvarchar(255)权限描述
create_timedatetime创建时间
update_timedatetime更新时间

2. 角色表(role)

字段名数据类型描述
idbigint角色ID
codevarchar(64)角色编码
namevarchar(128)角色名称
descriptionvarchar(255)角色描述
create_timedatetime创建时间
update_timedatetime更新时间

3. 角色权限关联表(role_permission)

字段名数据类型描述
idbigint关联ID
role_idbigint角色ID
permission_idbigint权限ID

4. 角色继承表(role_inheritance)

字段名数据类型描述
idbigint关联ID
parent_role_idbigint父角色ID
child_role_idbigint子角色ID

5. 用户角色关联表(user_role)

字段名数据类型描述
idbigint关联ID
user_idbigint用户ID
role_idbigint角色ID

技术实现

1. 核心实体类

// 权限实体
@Data
@Entity
@Table(name = "permission")
public class Permission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "code", unique = true, nullable = false, length = 64)
    private String code;
    
    @Column(name = "name", nullable = false, length = 128)
    private String name;
    
    @Column(name = "description", length = 255)
    private String description;
    
    @CreationTimestamp
    @Column(name = "create_time", nullable = false)
    private Date createTime;
    
    @UpdateTimestamp
    @Column(name = "update_time", nullable = false)
    private Date updateTime;
}

// 角色实体
@Data
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "code", unique = true, nullable = false, length = 64)
    private String code;
    
    @Column(name = "name", nullable = false, length = 128)
    private String name;
    
    @Column(name = "description", length = 255)
    private String description;
    
    @CreationTimestamp
    @Column(name = "create_time", nullable = false)
    private Date createTime;
    
    @UpdateTimestamp
    @Column(name = "update_time", nullable = false)
    private Date updateTime;
}

// 角色权限关联
@Data
@Entity
@Table(name = "role_permission")
public class RolePermission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "role_id", nullable = false)
    private Long roleId;
    
    @Column(name = "permission_id", nullable = false)
    private Long permissionId;
}

// 角色继承
@Data
@Entity
@Table(name = "role_inheritance")
public class RoleInheritance {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "parent_role_id", nullable = false)
    private Long parentRoleId;
    
    @Column(name = "child_role_id", nullable = false)
    private Long childRoleId;
}

// 用户角色关联
@Data
@Entity
@Table(name = "user_role")
public class UserRole {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "user_id", nullable = false)
    private Long userId;
    
    @Column(name = "role_id", nullable = false)
    private Long roleId;
}

2. 权限管理服务

@Service
@Slf4j
public class PermissionService {
    
    @Autowired
    private PermissionRepository permissionRepository;
    
    @Autowired
    private RoleRepository roleRepository;
    
    @Autowired
    private RolePermissionRepository rolePermissionRepository;
    
    @Autowired
    private RoleInheritanceRepository roleInheritanceRepository;
    
    @Autowired
    private UserRoleRepository userRoleRepository;
    
    /**
     * 获取用户的所有权限(包括继承的权限)
     */
    public Set<String> getUserPermissions(Long userId) {
        // 1. 获取用户的所有角色
        List<Long> roleIds = userRoleRepository.findByUserId(userId).stream()
                .map(UserRole::getRoleId)
                .collect(Collectors.toList());
        
        if (roleIds.isEmpty()) {
            return Collections.emptySet();
        }
        
        // 2. 递归获取所有继承的角色
        Set<Long> allRoleIds = new HashSet<>(roleIds);
        for (Long roleId : roleIds) {
            addInheritedRoles(roleId, allRoleIds);
        }
        
        // 3. 获取所有角色的权限
        Set<String> permissions = new HashSet<>();
        for (Long roleId : allRoleIds) {
            List<RolePermission> rolePermissions = rolePermissionRepository.findByRoleId(roleId);
            for (RolePermission rp : rolePermissions) {
                Permission permission = permissionRepository.findById(rp.getPermissionId()).orElse(null);
                if (permission != null) {
                    permissions.add(permission.getCode());
                }
            }
        }
        
        return permissions;
    }
    
    /**
     * 递归添加继承的角色
     */
    private void addInheritedRoles(Long roleId, Set<Long> allRoleIds) {
        List<RoleInheritance> inheritances = roleInheritanceRepository.findByChildRoleId(roleId);
        for (RoleInheritance inheritance : inheritances) {
            Long parentRoleId = inheritance.getParentRoleId();
            if (!allRoleIds.contains(parentRoleId)) {
                allRoleIds.add(parentRoleId);
                addInheritedRoles(parentRoleId, allRoleIds);
            }
        }
    }
    
    /**
     * 检查用户是否有指定权限
     */
    public boolean hasPermission(Long userId, String permissionCode) {
        Set<String> permissions = getUserPermissions(userId);
        return permissions.contains(permissionCode);
    }
    
    /**
     * 检查用户是否有指定权限列表中的任意一个
     */
    public boolean hasAnyPermission(Long userId, String... permissionCodes) {
        Set<String> permissions = getUserPermissions(userId);
        for (String permissionCode : permissionCodes) {
            if (permissions.contains(permissionCode)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 检查用户是否有指定权限列表中的所有权限
     */
    public boolean hasAllPermissions(Long userId, String... permissionCodes) {
        Set<String> permissions = getUserPermissions(userId);
        for (String permissionCode : permissionCodes) {
            if (!permissions.contains(permissionCode)) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * 给角色添加权限
     */
    public void addPermissionToRole(Long roleId, Long permissionId) {
        RolePermission rolePermission = new RolePermission();
        rolePermission.setRoleId(roleId);
        rolePermission.setPermissionId(permissionId);
        rolePermissionRepository.save(rolePermission);
    }
    
    /**
     * 给角色添加继承关系
     */
    public void addRoleInheritance(Long parentRoleId, Long childRoleId) {
        RoleInheritance inheritance = new RoleInheritance();
        inheritance.setParentRoleId(parentRoleId);
        inheritance.setChildRoleId(childRoleId);
        roleInheritanceRepository.save(inheritance);
    }
    
    /**
     * 给用户分配角色
     */
    public void assignRoleToUser(Long userId, Long roleId) {
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(roleId);
        userRoleRepository.save(userRole);
    }
}

3. 权限验证注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    
    /**
     * 权限编码
     */
    String[] value();
    
    /**
     * 是否需要所有权限
     */
    boolean all() default false;
    
}

4. 权限验证拦截器

@Component
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {
    
    @Autowired
    private PermissionService permissionService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                            Object handler) throws Exception {
        
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        RequirePermission requirePermission = handlerMethod.getMethodAnnotation(RequirePermission.class);
        
        if (requirePermission == null) {
            requirePermission = handlerMethod.getBeanType().getAnnotation(RequirePermission.class);
        }
        
        if (requirePermission == null) {
            return true;
        }
        
        // 获取当前用户ID
        Long userId = (Long) request.getAttribute("currentUserId");
        if (userId == null) {
            writeErrorResponse(response, HttpStatus.UNAUTHORIZED.value(), "请先登录");
            return false;
        }
        
        // 验证权限
        String[] permissions = requirePermission.value();
        boolean all = requirePermission.all();
        
        boolean hasPermission;
        if (all) {
            hasPermission = permissionService.hasAllPermissions(userId, permissions);
        } else {
            hasPermission = permissionService.hasAnyPermission(userId, permissions);
        }
        
        if (!hasPermission) {
            writeErrorResponse(response, HttpStatus.FORBIDDEN.value(), "权限不足");
            return false;
        }
        
        return true;
    }
    
    private void writeErrorResponse(HttpServletResponse response, int status, String message)
            throws IOException {
        response.setStatus(status);
        response.setContentType("application/json;charset=UTF-8");
        Result<String> result = Result.error(status, message);
        response.getWriter().write(JSON.toJSONString(result));
    }
}

5. 控制器示例

@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {
    
    // 需要 user:read 权限
    @GetMapping
    @RequirePermission("user:read")
    public Result<List<User>> listUsers() {
        // 业务逻辑
        return Result.success(new ArrayList<>());
    }
    
    // 需要 user:write 权限
    @PostMapping
    @RequirePermission("user:write")
    public Result<User> createUser(@RequestBody User user) {
        // 业务逻辑
        return Result.success(user);
    }
    
    // 需要 user:read 或 admin:read 权限
    @GetMapping("/{id}")
    @RequirePermission({"user:read", "admin:read"})
    public Result<User> getUser(@PathVariable Long id) {
        // 业务逻辑
        return Result.success(new User());
    }
    
    // 需要同时拥有 user:write 和 admin:write 权限
    @PutMapping("/{id}")
    @RequirePermission(value = {"user:write", "admin:write"}, all = true)
    public Result<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        // 业务逻辑
        return Result.success(user);
    }
}

@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderController {
    
    // 需要 order:create 权限
    @PostMapping
    @RequirePermission("order:create")
    public Result<Order> createOrder(@RequestBody Order order) {
        // 业务逻辑
        return Result.success(order);
    }
    
    // 需要 order:approve 权限
    @PutMapping("/{id}/approve")
    @RequirePermission("order:approve")
    public Result<Order> approveOrder(@PathVariable Long id) {
        // 业务逻辑
        return Result.success(new Order());
    }
}

6. 角色配置示例

@Service
public class RoleInitService {
    
    @Autowired
    private PermissionRepository permissionRepository;
    
    @Autowired
    private RoleRepository roleRepository;
    
    @Autowired
    private PermissionService permissionService;
    
    @PostConstruct
    public void init() {
        // 1. 创建权限
        createPermissions();
        
        // 2. 创建角色
        createRoles();
        
        // 3. 配置角色权限
        configureRolePermissions();
        
        // 4. 配置角色继承
        configureRoleInheritance();
    }
    
    private void createPermissions() {
        createPermission("user:read", "读取用户信息");
        createPermission("user:write", "写入用户信息");
        createPermission("order:create", "创建订单");
        createPermission("order:list", "查看订单列表");
        createPermission("order:approve", "审批订单");
        createPermission("admin:read", "管理员读取权限");
        createPermission("admin:write", "管理员写入权限");
    }
    
    private void createRoles() {
        createRole("ROLE_USER", "普通用户");
        createRole("ROLE_APPROVER", "审批员");
        createRole("ROLE_ADMIN", "管理员");
    }
    
    private void configureRolePermissions() {
        Role userRole = roleRepository.findByCode("ROLE_USER").orElse(null);
        Role approverRole = roleRepository.findByCode("ROLE_APPROVER").orElse(null);
        Role adminRole = roleRepository.findByCode("ROLE_ADMIN").orElse(null);
        
        if (userRole != null) {
            permissionService.addPermissionToRole(userRole.getId(), getPermissionId("user:read"));
            permissionService.addPermissionToRole(userRole.getId(), getPermissionId("order:create"));
        }
        
        if (approverRole != null) {
            permissionService.addPermissionToRole(approverRole.getId(), getPermissionId("order:list"));
            permissionService.addPermissionToRole(approverRole.getId(), getPermissionId("order:approve"));
        }
        
        if (adminRole != null) {
            permissionService.addPermissionToRole(adminRole.getId(), getPermissionId("admin:read"));
            permissionService.addPermissionToRole(adminRole.getId(), getPermissionId("admin:write"));
        }
    }
    
    private void configureRoleInheritance() {
        Role userRole = roleRepository.findByCode("ROLE_USER").orElse(null);
        Role approverRole = roleRepository.findByCode("ROLE_APPROVER").orElse(null);
        Role adminRole = roleRepository.findByCode("ROLE_ADMIN").orElse(null);
        
        // 审批员继承普通用户的权限
        if (userRole != null && approverRole != null) {
            permissionService.addRoleInheritance(userRole.getId(), approverRole.getId());
        }
        
        // 管理员继承审批员的权限
        if (approverRole != null && adminRole != null) {
            permissionService.addRoleInheritance(approverRole.getId(), adminRole.getId());
        }
    }
    
    private void createPermission(String code, String name) {
        if (permissionRepository.findByCode(code).isEmpty()) {
            Permission permission = new Permission();
            permission.setCode(code);
            permission.setName(name);
            permissionRepository.save(permission);
        }
    }
    
    private void createRole(String code, String name) {
        if (roleRepository.findByCode(code).isEmpty()) {
            Role role = new Role();
            role.setCode(code);
            role.setName(name);
            roleRepository.save(role);
        }
    }
    
    private Long getPermissionId(String code) {
        return permissionRepository.findByCode(code)
                .map(Permission::getId)
                .orElse(null);
    }
}

核心流程

1. 权限计算流程

  1. 获取用户角色:从数据库中获取用户关联的所有角色
  2. 递归获取继承角色:根据角色继承关系,递归获取所有父角色
  3. 合并权限:收集所有角色的权限,去重得到最终权限集
  4. 权限验证:检查用户是否拥有所需的权限

2. 角色继承关系

ROLE_ADMIN (管理员)
    ↑ 继承
ROLE_APPROVER (审批员)
    ↑ 继承
ROLE_USER (普通用户)
  • 普通用户:user:read, order:create
  • 审批员:继承普通用户的权限 + order:list, order:approve
  • 管理员:继承审批员的权限 + admin:read, admin:write

3. 多角色叠加示例

如果一个用户同时拥有 ROLE_USERROLE_APPROVER 角色:

  • 权限 = user:read, order:create (用户) + order:list, order:approve (审批员)
  • 结果 = user:read, order:create, order:list, order:approve

技术要点

1. 权限设计原则

  • 最小权限原则:只授予用户完成工作所需的最小权限
  • 权限粒度:权限粒度要适中,既不要太粗也不要太细
  • 权限命名规范:采用 资源:操作 的命名规范,清晰明了

2. 性能优化

  • 权限缓存:将用户权限缓存起来,减少数据库查询
  • 批量查询:批量获取角色和权限信息,减少数据库连接
  • 异步加载:对于复杂的权限计算,考虑使用异步加载

3. 安全性考虑

  • 权限验证:所有敏感操作都需要进行权限验证
  • 权限审计:记录权限相关的操作日志,便于审计
  • 权限撤销:当用户角色变更时,及时更新权限缓存

4. 可扩展性

  • 动态权限:支持运行时动态添加和修改权限
  • 权限分组:支持权限分组,便于管理
  • 自定义权限:支持业务自定义权限规则

最佳实践

1. 权限设计

  • 分层设计:将权限分为功能权限、数据权限、操作权限等
  • 模块化:按业务模块划分权限,便于管理
  • 标准化:建立统一的权限命名规范

2. 角色管理

  • 角色模板:预设常用角色模板,快速配置
  • 角色组合:支持用户拥有多个角色,权限叠加
  • 角色继承:合理设计角色继承关系,减少重复配置

3. 权限验证

  • 注解方式:使用注解方式进行权限验证,代码简洁
  • 编程方式:对于复杂的权限验证,使用编程方式
  • 全局验证:对敏感操作进行全局权限验证

4. 管理界面

  • 权限管理:提供可视化的权限管理界面
  • 角色管理:支持角色的创建、编辑、删除
  • 用户授权:支持对用户进行批量授权

常见问题

1. 权限冲突

问题:用户同时拥有多个角色,权限可能存在冲突

解决方案

  • 权限是所有角色权限的并集
  • 对于冲突的权限,以更高级的权限为准

2. 权限继承循环

问题:角色继承关系可能形成循环

解决方案

  • 在添加继承关系时进行循环检测
  • 使用深度优先搜索检测循环依赖

3. 权限缓存过期

问题:权限变更后,缓存没有及时更新

解决方案

  • 权限变更时主动清除相关缓存
  • 设置合理的缓存过期时间

4. 性能问题

问题:权限计算过程中数据库查询过多

解决方案

  • 使用缓存减少数据库查询
  • 批量查询优化
  • 异步加载复杂权限

代码优化建议

1. 权限缓存优化

@Service
@Slf4j
public class PermissionCacheService {
    
    @Autowired
    private PermissionService permissionService;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String PERMISSION_CACHE_KEY = "permission:user:%s";
    private static final long CACHE_EXPIRE_TIME = 3600L; // 1小时
    
    /**
     * 获取用户权限(带缓存)
     */
    public Set<String> getUserPermissions(Long userId) {
        String cacheKey = String.format(PERMISSION_CACHE_KEY, userId);
        
        // 从缓存获取
        String cachedPermissions = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.hasText(cachedPermissions)) {
            return JSON.parseObject(cachedPermissions, new TypeReference<Set<String>>() {});
        }
        
        // 从数据库获取
        Set<String> permissions = permissionService.getUserPermissions(userId);
        
        // 存入缓存
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(permissions), CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
        
        return permissions;
    }
    
    /**
     * 清除用户权限缓存
     */
    public void clearUserPermissionCache(Long userId) {
        String cacheKey = String.format(PERMISSION_CACHE_KEY, userId);
        redisTemplate.delete(cacheKey);
    }
    
    /**
     * 清除所有权限缓存
     */
    public void clearAllPermissionCache() {
        Set<String> keys = redisTemplate.keys("permission:user:*");
        if (!CollectionUtils.isEmpty(keys)) {
            redisTemplate.delete(keys);
        }
    }
}

2. 权限验证增强

@Service
@Slf4j
public class PermissionCheckService {
    
    @Autowired
    private PermissionCacheService permissionCacheService;
    
    /**
     * 检查用户是否有指定权限
     */
    public boolean checkPermission(Long userId, String permissionCode) {
        Set<String> permissions = permissionCacheService.getUserPermissions(userId);
        return permissions.contains(permissionCode);
    }
    
    /**
     * 检查用户是否有指定资源的操作权限
     */
    public boolean checkResourcePermission(Long userId, String resource, String action) {
        String permissionCode = resource + ":" + action;
        return checkPermission(userId, permissionCode);
    }
    
    /**
     * 检查用户是否有数据权限
     */
    public boolean checkDataPermission(Long userId, Long resourceId, String resourceType) {
        // 这里可以实现更复杂的数据权限逻辑
        // 例如:用户只能访问自己的订单,部门主管可以访问部门内的所有订单等
        return true;
    }
}

总结

本文介绍了一种灵活的权限管理方案:多角色权限叠加 + 权限继承,其核心优势:

  1. 灵活性:支持多角色叠加,权限自动合并
  2. 可维护性:通过角色继承,减少重复配置
  3. 可扩展性:支持动态添加权限和角色
  4. 安全性:细粒度的权限控制,保证系统安全

这种方案适用于复杂的企业级应用,可以根据业务需求灵活调整权限体系。通过合理的权限设计和角色继承关系,可以大大减少权限管理的复杂度,提高系统的可维护性。

互动话题

  1. 你在实际项目中使用过哪些权限管理方案?
  2. 对于复杂的权限场景,你有什么好的解决方案?
  3. 你认为权限管理中最容易出错的地方是什么?

欢迎在评论区交流讨论!


欢迎关注公众号:服务端技术精选,获取更多技术分享和经验。


标题:SpringBoot + 多角色权限叠加 + 权限继承:管理员 = 普通用户 + 审批权限,灵活组合
作者:jiangyi
地址:http://jiangyi.space/articles/2026/03/18/1773584262666.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消