第468集设计一个权限系统(RBAC/ABAC/数据权限)
|字数总计:4.6k|阅读时长:21分钟|阅读量:
设计一个权限系统(RBAC/ABAC/数据权限)
1. 概述
1.1 权限系统的重要性
权限系统是保障系统安全的核心组件,负责控制用户对系统资源的访问权限。一个完善的权限系统能够:
- 保障系统安全:防止未授权访问
- 精细权限控制:支持细粒度的权限管理
- 灵活权限配置:支持多种权限模型
- 便于管理维护:权限配置和管理简单
1.2 权限模型
主流权限模型:
- RBAC(Role-Based Access Control):基于角色的访问控制
- ABAC(Attribute-Based Access Control):基于属性的访问控制
- 数据权限:基于数据的访问控制
1.3 本文内容结构
本文将从以下几个方面全面解析权限系统:
- 权限系统概述:权限模型、核心概念
- RBAC设计:角色、权限、用户关系
- ABAC设计:属性、策略、规则引擎
- 数据权限:行级权限、列级权限、字段权限
- 架构设计:整体架构、模块设计
- 实现方案:完整实现代码
- 实战案例:实际应用场景
2. 权限系统概述
2.1 核心概念
2.1.1 用户(User)
用户:系统的使用者,权限的最终授予对象。
用户属性:
2.1.2 角色(Role)
角色:权限的集合,用于简化权限管理。
角色特点:
- 一个用户可以有多个角色
- 一个角色可以有多个权限
- 角色可以继承
2.1.3 权限(Permission)
权限:对资源的访问权限。
权限类型:
- 功能权限:菜单、按钮、接口
- 数据权限:数据范围、字段权限
- 操作权限:增删改查
2.1.4 资源(Resource)
资源:系统中需要保护的对象。
资源类型:
2.2 权限模型对比
2.2.1 RBAC vs ABAC
| 维度 |
RBAC |
ABAC |
| 控制粒度 |
粗粒度 |
细粒度 |
| 灵活性 |
中等 |
高 |
| 复杂度 |
低 |
高 |
| 适用场景 |
传统企业应用 |
复杂业务场景 |
| 性能 |
高 |
中等 |
2.2.2 选择建议
RBAC适用:
ABAC适用:
3. RBAC设计
3.1 RBAC模型
3.1.1 基础RBAC(RBAC0)
模型:
特点:
3.1.2 角色继承(RBAC1)
模型:
特点:
3.1.3 约束RBAC(RBAC2)
模型:
特点:
3.1.4 统一RBAC(RBAC3)
模型:
特点:
- 结合RBAC1和RBAC2
- 支持角色继承和约束
- 最完整的RBAC模型
3.2 数据库设计
3.2.1 用户表
1 2 3 4 5 6 7 8 9 10 11 12 13
| CREATE TABLE `sys_user` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` VARCHAR(50) NOT NULL COMMENT '用户名', `password` VARCHAR(100) NOT NULL COMMENT '密码', `real_name` VARCHAR(50) COMMENT '真实姓名', `email` VARCHAR(100) COMMENT '邮箱', `phone` VARCHAR(20) COMMENT '手机号', `dept_id` BIGINT(20) COMMENT '部门ID', `status` TINYINT(1) DEFAULT 1 COMMENT '状态:1-启用,0-禁用', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
3.2.2 角色表
1 2 3 4 5 6 7 8 9 10 11
| CREATE TABLE `sys_role` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_code` VARCHAR(50) NOT NULL COMMENT '角色编码', `role_name` VARCHAR(50) NOT NULL COMMENT '角色名称', `description` VARCHAR(200) COMMENT '描述', `parent_id` BIGINT(20) COMMENT '父角色ID(角色继承)', `status` TINYINT(1) DEFAULT 1 COMMENT '状态:1-启用,0-禁用', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_role_code` (`role_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
|
3.2.3 权限表
1 2 3 4 5 6 7 8 9 10 11 12
| CREATE TABLE `sys_permission` ( `id` BIGINT(20) 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_id` VARCHAR(100) COMMENT '资源ID', `parent_id` BIGINT(20) COMMENT '父权限ID', `sort_order` INT DEFAULT 0 COMMENT '排序', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_permission_code` (`permission_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';
|
3.2.4 用户角色关联表
1 2 3 4 5 6 7 8 9 10
| CREATE TABLE `sys_user_role` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_id` BIGINT(20) NOT NULL COMMENT '用户ID', `role_id` BIGINT(20) NOT NULL COMMENT '角色ID', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 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='用户角色关联表';
|
3.2.5 角色权限关联表
1 2 3 4 5 6 7 8 9 10
| CREATE TABLE `sys_role_permission` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `role_id` BIGINT(20) NOT NULL COMMENT '角色ID', `permission_id` BIGINT(20) NOT NULL COMMENT '权限ID', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 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='角色权限关联表';
|
3.3 RBAC实现
3.3.1 权限服务
权限服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| @Service public class PermissionService { @Autowired private UserMapper userMapper; @Autowired private RoleMapper roleMapper; @Autowired private PermissionMapper permissionMapper; @Autowired private RedisTemplate<String, String> redisTemplate;
public Set<String> getUserPermissions(Long userId) { String cacheKey = "user:permissions:" + userId; String cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { return JSON.parseObject(cached, new TypeReference<Set<String>>() {}); } Set<String> permissions = new HashSet<>(); List<Role> roles = roleMapper.selectByUserId(userId); for (Role role : roles) { List<Permission> rolePermissions = permissionMapper.selectByRoleId(role.getId()); for (Permission permission : rolePermissions) { permissions.add(permission.getPermissionCode()); } if (role.getParentId() != null) { addInheritedPermissions(role.getParentId(), permissions); } } redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(permissions), 1, TimeUnit.HOURS); return permissions; }
private void addInheritedPermissions(Long parentRoleId, Set<String> permissions) { Role parentRole = roleMapper.selectById(parentRoleId); if (parentRole != null) { List<Permission> parentPermissions = permissionMapper.selectByRoleId(parentRoleId); for (Permission permission : parentPermissions) { permissions.add(permission.getPermissionCode()); } if (parentRole.getParentId() != null) { addInheritedPermissions(parentRole.getParentId(), permissions); } } }
public boolean hasPermission(Long userId, String permissionCode) { Set<String> permissions = getUserPermissions(userId); return permissions.contains(permissionCode); } }
|
3.3.2 权限拦截器
权限拦截器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Component public class PermissionInterceptor implements HandlerInterceptor { @Autowired private PermissionService permissionService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Long userId = getCurrentUserId(request); if (userId == null) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } String permissionCode = getPermissionCode(request, handler); if (permissionCode == null) { return true; } if (!permissionService.hasPermission(userId, permissionCode)) { response.setStatus(HttpStatus.FORBIDDEN.value()); response.getWriter().write("无权限访问"); return false; } return true; } private String getPermissionCode(HttpServletRequest request, Object handler) { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; RequirePermission annotation = handlerMethod.getMethodAnnotation(RequirePermission.class); if (annotation != null) { return annotation.value(); } } return null; } }
|
3.3.3 权限注解
权限注解:
1 2 3 4 5
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequirePermission { String value(); }
|
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @RestController @RequestMapping("/api/user") public class UserController { @RequirePermission("user:query") @GetMapping("/{id}") public Response<User> getUser(@PathVariable Long id) { return Response.success(userService.getUser(id)); } @RequirePermission("user:create") @PostMapping public Response<String> createUser(@RequestBody User user) { return Response.success("创建成功"); } @RequirePermission("user:update") @PutMapping("/{id}") public Response<String> updateUser(@PathVariable Long id, @RequestBody User user) { return Response.success("更新成功"); } @RequirePermission("user:delete") @DeleteMapping("/{id}") public Response<String> deleteUser(@PathVariable Long id) { return Response.success("删除成功"); } }
|
4. ABAC设计
4.1 ABAC模型
4.1.1 核心概念
ABAC(Attribute-Based Access Control):基于属性的访问控制。
核心要素:
- Subject(主体):用户及其属性
- Resource(资源):资源及其属性
- Action(操作):操作类型
- Environment(环境):环境属性
4.1.2 策略模型
策略结构:
条件:
- Subject属性
- Resource属性
- Action属性
- Environment属性
决策:
4.2 数据库设计
4.2.1 策略表
1 2 3 4 5 6 7 8 9 10 11
| CREATE TABLE `sys_policy` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '策略ID', `policy_name` VARCHAR(100) NOT NULL COMMENT '策略名称', `description` VARCHAR(200) COMMENT '描述', `effect` VARCHAR(10) NOT NULL COMMENT '效果:Allow,Deny', `conditions` TEXT NOT NULL COMMENT '条件(JSON)', `priority` INT DEFAULT 0 COMMENT '优先级', `status` TINYINT(1) DEFAULT 1 COMMENT '状态:1-启用,0-禁用', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='策略表';
|
4.2.2 属性表
1 2 3 4 5 6 7 8 9 10
| CREATE TABLE `sys_attribute` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '属性ID', `attribute_name` VARCHAR(50) NOT NULL COMMENT '属性名称', `attribute_type` VARCHAR(20) NOT NULL COMMENT '属性类型:subject,resource,action,environment', `data_type` VARCHAR(20) NOT NULL COMMENT '数据类型:string,number,boolean,date', `description` VARCHAR(200) COMMENT '描述', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_attribute_name` (`attribute_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='属性表';
|
4.3 ABAC实现
4.3.1 策略引擎
策略引擎:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| @Service public class PolicyEngine { @Autowired private PolicyMapper policyMapper;
public AccessDecision evaluate(AccessRequest request) { List<Policy> policies = policyMapper.selectActivePolicies(); policies.sort(Comparator.comparing(Policy::getPriority).reversed()); for (Policy policy : policies) { if (evaluatePolicy(policy, request)) { return new AccessDecision(policy.getEffect(), policy.getId()); } } return new AccessDecision("Deny", null); }
private boolean evaluatePolicy(Policy policy, AccessRequest request) { JSONObject conditions = JSON.parseObject(policy.getConditions()); for (String key : conditions.keySet()) { Object condition = conditions.get(key); if (!evaluateCondition(key, condition, request)) { return false; } } return true; }
private boolean evaluateCondition(String attribute, Object condition, AccessRequest request) { Object attributeValue = getAttributeValue(attribute, request); if (condition instanceof Map) { Map<String, Object> conditionMap = (Map<String, Object>) condition; String operator = (String) conditionMap.get("operator"); Object expectedValue = conditionMap.get("value"); return evaluateOperator(operator, attributeValue, expectedValue); } return false; }
private Object getAttributeValue(String attribute, AccessRequest request) { if (attribute.startsWith("subject.")) { return request.getSubject().getAttribute(attribute.substring(8)); } else if (attribute.startsWith("resource.")) { return request.getResource().getAttribute(attribute.substring(9)); } else if (attribute.startsWith("action.")) { return request.getAction().getAttribute(attribute.substring(7)); } else if (attribute.startsWith("environment.")) { return request.getEnvironment().getAttribute(attribute.substring(12)); } return null; }
private boolean evaluateOperator(String operator, Object actual, Object expected) { switch (operator) { case "equals": return Objects.equals(actual, expected); case "not_equals": return !Objects.equals(actual, expected); case "in": if (expected instanceof List) { return ((List<?>) expected).contains(actual); } return false; case "not_in": if (expected instanceof List) { return !((List<?>) expected).contains(actual); } return false; case "greater_than": return compareNumbers(actual, expected) > 0; case "less_than": return compareNumbers(actual, expected) < 0; case "contains": if (actual instanceof String && expected instanceof String) { return ((String) actual).contains((String) expected); } return false; default: return false; } } }
|
4.3.2 访问请求
访问请求模型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class AccessRequest { private Subject subject; private Resource resource; private Action action; private Environment environment; }
public class Subject { private Long userId; private String username; private String deptId; private String role; private Map<String, Object> attributes; public Object getAttribute(String key) { return attributes.get(key); } }
public class Resource { private String resourceType; private String resourceId; private String ownerId; private Map<String, Object> attributes; public Object getAttribute(String key) { return attributes.get(key); } }
public class Action { private String actionType; private Map<String, Object> attributes; public Object getAttribute(String key) { return attributes.get(key); } }
public class Environment { private String ip; private String time; private Map<String, Object> attributes; public Object getAttribute(String key) { return attributes.get(key); } }
|
4.3.3 ABAC拦截器
ABAC拦截器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Component public class AbacInterceptor implements HandlerInterceptor { @Autowired private PolicyEngine policyEngine; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { AccessRequest accessRequest = buildAccessRequest(request, handler); AccessDecision decision = policyEngine.evaluate(accessRequest); if ("Deny".equals(decision.getEffect())) { response.setStatus(HttpStatus.FORBIDDEN.value()); response.getWriter().write("无权限访问"); return false; } return true; } private AccessRequest buildAccessRequest(HttpServletRequest request, Object handler) { AccessRequest accessRequest = new AccessRequest(); Subject subject = new Subject(); Long userId = getCurrentUserId(request); User user = userService.getUser(userId); subject.setUserId(userId); subject.setUsername(user.getUsername()); subject.setDeptId(user.getDeptId()); subject.setRole(user.getRole()); accessRequest.setSubject(subject); Resource resource = new Resource(); resource.setResourceType(getResourceType(handler)); resource.setResourceId(getResourceId(request)); accessRequest.setResource(resource); Action action = new Action(); action.setActionType(getActionType(request)); accessRequest.setAction(action); Environment environment = new Environment(); environment.setIp(getClientIp(request)); environment.setTime(String.valueOf(System.currentTimeMillis())); accessRequest.setEnvironment(environment); return accessRequest; } }
|
5. 数据权限
5.1 数据权限类型
5.1.1 行级权限
行级权限:控制用户可以看到哪些数据行。
实现方式:
5.1.2 列级权限
列级权限:控制用户可以看到哪些数据列。
实现方式:
5.1.3 字段权限
字段权限:控制用户对字段的操作权限。
实现方式:
5.2 数据权限设计
5.2.1 数据范围
数据范围类型:
- 全部数据:可以看到所有数据
- 本部门数据:只能看到本部门数据
- 本部门及子部门数据:可以看到本部门及子部门数据
- 本人数据:只能看到自己的数据
- 自定义数据:自定义数据范围
5.2.2 数据库设计
数据权限表:
1 2 3 4 5 6 7 8 9 10 11 12
| CREATE TABLE `sys_data_permission` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_id` BIGINT(20) COMMENT '用户ID', `role_id` BIGINT(20) COMMENT '角色ID', `resource_type` VARCHAR(50) NOT NULL COMMENT '资源类型', `data_scope` VARCHAR(20) NOT NULL COMMENT '数据范围:all,dept,dept_and_children,self,custom', `dept_ids` TEXT COMMENT '部门ID列表(JSON,custom类型使用)', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_role_id` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据权限表';
|
5.3 数据权限实现
5.3.1 数据权限服务
数据权限服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| @Service public class DataPermissionService { @Autowired private DataPermissionMapper dataPermissionMapper; @Autowired private DeptService deptService;
public DataScope getUserDataScope(Long userId, String resourceType) { DataPermission userPermission = dataPermissionMapper.selectByUserId(userId, resourceType); if (userPermission != null) { return buildDataScope(userPermission); } List<Role> roles = roleMapper.selectByUserId(userId); for (Role role : roles) { DataPermission rolePermission = dataPermissionMapper.selectByRoleId(role.getId(), resourceType); if (rolePermission != null) { return buildDataScope(rolePermission); } } return new DataScope("self", null); }
private DataScope buildDataScope(DataPermission permission) { DataScope scope = new DataScope(); scope.setDataScope(permission.getDataScope()); if ("custom".equals(permission.getDataScope())) { List<Long> deptIds = JSON.parseArray(permission.getDeptIds(), Long.class); scope.setDeptIds(deptIds); } return scope; }
public String buildWhereClause(Long userId, String resourceType, String tableAlias) { DataScope dataScope = getUserDataScope(userId, resourceType); switch (dataScope.getDataScope()) { case "all": return ""; case "dept": User user = userService.getUser(userId); return String.format("%s.dept_id = %d", tableAlias, user.getDeptId()); case "dept_and_children": User user2 = userService.getUser(userId); List<Long> deptIds = deptService.getDeptAndChildrenIds(user2.getDeptId()); return String.format("%s.dept_id IN (%s)", tableAlias, deptIds.stream().map(String::valueOf).collect(Collectors.joining(","))); case "self": return String.format("%s.user_id = %d", tableAlias, userId); case "custom": List<Long> customDeptIds = dataScope.getDeptIds(); return String.format("%s.dept_id IN (%s)", tableAlias, customDeptIds.stream().map(String::valueOf).collect(Collectors.joining(","))); default: return String.format("%s.user_id = %d", tableAlias, userId); } } }
|
5.3.2 MyBatis拦截器
数据权限拦截器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) @Component public class DataPermissionInterceptor implements Interceptor { @Autowired private DataPermissionService dataPermissionService; @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; if (needDataPermission(mappedStatement)) { Long userId = getCurrentUserId(); String resourceType = getResourceType(mappedStatement); String whereClause = dataPermissionService.buildWhereClause(userId, resourceType, "t"); if (StringUtils.isNotBlank(whereClause)) { BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]); String sql = boundSql.getSql(); sql = addWhereClause(sql, whereClause); BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject()); MappedStatement newMappedStatement = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql)); invocation.getArgs()[0] = newMappedStatement; } } return invocation.proceed(); } private String addWhereClause(String sql, String whereClause) { if (sql.toUpperCase().contains("WHERE")) { return sql + " AND " + whereClause; } else { return sql + " WHERE " + whereClause; } } }
|
6. 架构设计
6.1 整体架构
6.1.1 架构图
1 2 3 4 5 6 7 8 9 10 11 12 13
| 业务系统 ↓ 权限拦截器 ↓ 权限服务 ↓ ├──→ RBAC服务(角色权限) ├──→ ABAC服务(属性权限) └──→ 数据权限服务(数据权限) ↓ ├──→ Redis(权限缓存) ├──→ MySQL(权限数据) └──→ 规则引擎(ABAC策略评估)
|
6.2 权限服务
6.2.1 统一权限接口
权限接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public interface PermissionService {
boolean hasPermission(Long userId, String permissionCode);
boolean hasDataPermission(Long userId, String resourceType, String resourceId);
Set<String> getUserPermissions(Long userId); }
|
6.2.2 权限服务实现
统一权限服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Service public class UnifiedPermissionService implements PermissionService { @Autowired private RbacPermissionService rbacPermissionService; @Autowired private AbacPermissionService abacPermissionService; @Autowired private DataPermissionService dataPermissionService; @Override public boolean hasPermission(Long userId, String permissionCode) { if (rbacPermissionService.hasPermission(userId, permissionCode)) { return true; } return false; } @Override public boolean hasDataPermission(Long userId, String resourceType, String resourceId) { return dataPermissionService.hasDataPermission(userId, resourceType, resourceId); } @Override public Set<String> getUserPermissions(Long userId) { return rbacPermissionService.getUserPermissions(userId); } }
|
7. 实战案例
7.1 案例1:多租户系统权限
7.1.1 场景
需求:多租户系统,每个租户有独立的权限体系。
实现:
1 2 3 4 5 6 7 8 9
| @Service public class TenantPermissionService { public Set<String> getUserPermissions(Long tenantId, Long userId) { String cacheKey = "tenant:permissions:" + tenantId + ":" + userId; return permissionService.getUserPermissions(tenantId, userId); } }
|
7.2 案例2:动态权限控制
7.2.1 场景
需求:根据业务规则动态控制权限。
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class DynamicPermissionService { @Autowired private PolicyEngine policyEngine; public boolean checkPermission(Long userId, String resource, String action) { AccessRequest request = buildAccessRequest(userId, resource, action); AccessDecision decision = policyEngine.evaluate(request); return "Allow".equals(decision.getEffect()); } }
|
8. 总结
8.1 核心要点
- RBAC设计:角色、权限、用户关系
- ABAC设计:属性、策略、规则引擎
- 数据权限:行级权限、列级权限、字段权限
- 架构设计:统一权限接口、权限服务
- 实现方案:完整的代码实现
8.2 关键设计
- RBAC:简单高效,适合传统应用
- ABAC:灵活强大,适合复杂场景
- 数据权限:细粒度控制,保障数据安全
- 统一接口:统一权限接口,便于使用
8.3 最佳实践
- 权限缓存:Redis缓存权限,提高性能
- 权限拦截:拦截器统一处理,减少代码侵入
- 数据权限:SQL拦截器自动过滤,透明实现
- 权限管理:权限配置界面,便于管理
相关文章: