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)
| 字段名 | 数据类型 | 描述 |
|---|---|---|
| id | bigint | 权限ID |
| code | varchar(64) | 权限编码 |
| name | varchar(128) | 权限名称 |
| description | varchar(255) | 权限描述 |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
2. 角色表(role)
| 字段名 | 数据类型 | 描述 |
|---|---|---|
| id | bigint | 角色ID |
| code | varchar(64) | 角色编码 |
| name | varchar(128) | 角色名称 |
| description | varchar(255) | 角色描述 |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
3. 角色权限关联表(role_permission)
| 字段名 | 数据类型 | 描述 |
|---|---|---|
| id | bigint | 关联ID |
| role_id | bigint | 角色ID |
| permission_id | bigint | 权限ID |
4. 角色继承表(role_inheritance)
| 字段名 | 数据类型 | 描述 |
|---|---|---|
| id | bigint | 关联ID |
| parent_role_id | bigint | 父角色ID |
| child_role_id | bigint | 子角色ID |
5. 用户角色关联表(user_role)
| 字段名 | 数据类型 | 描述 |
|---|---|---|
| id | bigint | 关联ID |
| user_id | bigint | 用户ID |
| role_id | bigint | 角色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. 权限计算流程
- 获取用户角色:从数据库中获取用户关联的所有角色
- 递归获取继承角色:根据角色继承关系,递归获取所有父角色
- 合并权限:收集所有角色的权限,去重得到最终权限集
- 权限验证:检查用户是否拥有所需的权限
2. 角色继承关系
ROLE_ADMIN (管理员)
↑ 继承
ROLE_APPROVER (审批员)
↑ 继承
ROLE_USER (普通用户)
- 普通用户:
user:read,order:create - 审批员:继承普通用户的权限 +
order:list,order:approve - 管理员:继承审批员的权限 +
admin:read,admin:write
3. 多角色叠加示例
如果一个用户同时拥有 ROLE_USER 和 ROLE_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;
}
}
总结
本文介绍了一种灵活的权限管理方案:多角色权限叠加 + 权限继承,其核心优势:
- 灵活性:支持多角色叠加,权限自动合并
- 可维护性:通过角色继承,减少重复配置
- 可扩展性:支持动态添加权限和角色
- 安全性:细粒度的权限控制,保证系统安全
这种方案适用于复杂的企业级应用,可以根据业务需求灵活调整权限体系。通过合理的权限设计和角色继承关系,可以大大减少权限管理的复杂度,提高系统的可维护性。
互动话题
- 你在实际项目中使用过哪些权限管理方案?
- 对于复杂的权限场景,你有什么好的解决方案?
- 你认为权限管理中最容易出错的地方是什么?
欢迎在评论区交流讨论!
欢迎关注公众号:服务端技术精选,获取更多技术分享和经验。
标题:SpringBoot + 多角色权限叠加 + 权限继承:管理员 = 普通用户 + 审批权限,灵活组合
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/03/18/1773584262666.html
公众号:服务端技术精选
- 背景:权限管理的痛点
- 核心概念
- 1. 权限(Permission)
- 2. 角色(Role)
- 3. 角色继承(Role Inheritance)
- 4. 多角色叠加(Multiple Role Overlay)
- 架构设计
- 系统架构
- 数据模型
- 1. 权限表(permission)
- 2. 角色表(role)
- 3. 角色权限关联表(role_permission)
- 4. 角色继承表(role_inheritance)
- 5. 用户角色关联表(user_role)
- 技术实现
- 1. 核心实体类
- 2. 权限管理服务
- 3. 权限验证注解
- 4. 权限验证拦截器
- 5. 控制器示例
- 6. 角色配置示例
- 核心流程
- 1. 权限计算流程
- 2. 角色继承关系
- 3. 多角色叠加示例
- 技术要点
- 1. 权限设计原则
- 2. 性能优化
- 3. 安全性考虑
- 4. 可扩展性
- 最佳实践
- 1. 权限设计
- 2. 角色管理
- 3. 权限验证
- 4. 管理界面
- 常见问题
- 1. 权限冲突
- 2. 权限继承循环
- 3. 权限缓存过期
- 4. 性能问题
- 代码优化建议
- 1. 权限缓存优化
- 2. 权限验证增强
- 总结
- 互动话题
评论
0 评论