设计一个权限系统(RBAC/ABAC/数据权限)

1. 概述

1.1 权限系统的重要性

权限系统是保障系统安全的核心组件,负责控制用户对系统资源的访问权限。一个完善的权限系统能够:

  • 保障系统安全:防止未授权访问
  • 精细权限控制:支持细粒度的权限管理
  • 灵活权限配置:支持多种权限模型
  • 便于管理维护:权限配置和管理简单

1.2 权限模型

主流权限模型

  • RBAC(Role-Based Access Control):基于角色的访问控制
  • ABAC(Attribute-Based Access Control):基于属性的访问控制
  • 数据权限:基于数据的访问控制

1.3 本文内容结构

本文将从以下几个方面全面解析权限系统:

  1. 权限系统概述:权限模型、核心概念
  2. RBAC设计:角色、权限、用户关系
  3. ABAC设计:属性、策略、规则引擎
  4. 数据权限:行级权限、列级权限、字段权限
  5. 架构设计:整体架构、模块设计
  6. 实现方案:完整实现代码
  7. 实战案例:实际应用场景

2. 权限系统概述

2.1 核心概念

2.1.1 用户(User)

用户:系统的使用者,权限的最终授予对象。

用户属性

  • 用户ID
  • 用户名
  • 部门
  • 职位
  • 其他属性

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)

模型

1
用户 → 角色 → 权限

特点

  • 用户分配角色
  • 角色分配权限
  • 简单直接

3.1.2 角色继承(RBAC1)

模型

1
用户 → 角色(可继承) → 权限

特点

  • 角色可以继承
  • 子角色继承父角色权限
  • 支持角色层次

3.1.3 约束RBAC(RBAC2)

模型

1
用户 → 角色(有约束) → 权限

特点

  • 角色分配有约束
  • 互斥角色
  • 基数约束

3.1.4 统一RBAC(RBAC3)

模型

1
用户 → 角色(继承+约束) → 权限

特点

  • 结合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) {
// 1. 查询缓存
String cacheKey = "user:permissions:" + userId;
String cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return JSON.parseObject(cached, new TypeReference<Set<String>>() {});
}

// 2. 查询数据库
Set<String> permissions = new HashSet<>();

// 2.1 获取用户角色
List<Role> roles = roleMapper.selectByUserId(userId);

// 2.2 获取角色权限
for (Role role : roles) {
List<Permission> rolePermissions = permissionMapper.selectByRoleId(role.getId());
for (Permission permission : rolePermissions) {
permissions.add(permission.getPermissionCode());
}

// 2.3 处理角色继承
if (role.getParentId() != null) {
addInheritedPermissions(role.getParentId(), permissions);
}
}

// 3. 写入缓存
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 {
// 1. 获取当前用户
Long userId = getCurrentUserId(request);
if (userId == null) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}

// 2. 获取请求的权限编码
String permissionCode = getPermissionCode(request, handler);
if (permissionCode == null) {
// 无需权限验证
return true;
}

// 3. 检查权限
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 策略模型

策略结构

1
IF (条件) THEN (决策)

条件

  • Subject属性
  • Resource属性
  • Action属性
  • Environment属性

决策

  • Allow(允许)
  • Deny(拒绝)

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) {
// 1. 获取所有策略
List<Policy> policies = policyMapper.selectActivePolicies();

// 2. 按优先级排序
policies.sort(Comparator.comparing(Policy::getPriority).reversed());

// 3. 评估策略
for (Policy policy : policies) {
if (evaluatePolicy(policy, request)) {
return new AccessDecision(policy.getEffect(), policy.getId());
}
}

// 4. 默认拒绝
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; // 环境

// getters and setters
}

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 {
// 1. 构建访问请求
AccessRequest accessRequest = buildAccessRequest(request, handler);

// 2. 评估访问请求
AccessDecision decision = policyEngine.evaluate(accessRequest);

// 3. 根据决策处理
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 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 resource = new Resource();
resource.setResourceType(getResourceType(handler));
resource.setResourceId(getResourceId(request));
accessRequest.setResource(resource);

// 构建Action
Action action = new Action();
action.setActionType(getActionType(request));
accessRequest.setAction(action);

// 构建Environment
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 行级权限

行级权限:控制用户可以看到哪些数据行。

实现方式

  • SQL WHERE条件
  • 数据范围过滤
  • 动态SQL

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) {
// 1. 查询用户数据权限
DataPermission userPermission = dataPermissionMapper.selectByUserId(userId, resourceType);
if (userPermission != null) {
return buildDataScope(userPermission);
}

// 2. 查询角色数据权限
List<Role> roles = roleMapper.selectByUserId(userId);
for (Role role : roles) {
DataPermission rolePermission = dataPermissionMapper.selectByRoleId(role.getId(), resourceType);
if (rolePermission != null) {
return buildDataScope(rolePermission);
}
}

// 3. 默认:只能看自己的数据
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;
}

/**
* 构建SQL WHERE条件
*/
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 {
// 1. 获取MappedStatement
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];

// 2. 检查是否需要数据权限过滤
if (needDataPermission(mappedStatement)) {
// 3. 获取当前用户
Long userId = getCurrentUserId();

// 4. 获取资源类型
String resourceType = getResourceType(mappedStatement);

// 5. 构建WHERE条件
String whereClause = dataPermissionService.buildWhereClause(userId, resourceType, "t");

// 6. 修改SQL
if (StringUtils.isNotBlank(whereClause)) {
BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
String sql = boundSql.getSql();
sql = addWhereClause(sql, whereClause);

// 创建新的BoundSql
BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), sql,
boundSql.getParameterMappings(), boundSql.getParameterObject());

// 创建新的MappedStatement
MappedStatement newMappedStatement = copyFromMappedStatement(mappedStatement,
new BoundSqlSqlSource(newBoundSql));
invocation.getArgs()[0] = newMappedStatement;
}
}

return invocation.proceed();
}

private String addWhereClause(String sql, String whereClause) {
// 在SQL中添加WHERE条件
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) {
// 1. 先检查RBAC权限
if (rbacPermissionService.hasPermission(userId, permissionCode)) {
return true;
}

// 2. 再检查ABAC权限
// 需要构建AccessRequest
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;
// 查询权限(包含租户ID)
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
AccessRequest request = buildAccessRequest(userId, resource, action);

// 评估策略
AccessDecision decision = policyEngine.evaluate(request);

return "Allow".equals(decision.getEffect());
}
}

8. 总结

8.1 核心要点

  1. RBAC设计:角色、权限、用户关系
  2. ABAC设计:属性、策略、规则引擎
  3. 数据权限:行级权限、列级权限、字段权限
  4. 架构设计:统一权限接口、权限服务
  5. 实现方案:完整的代码实现

8.2 关键设计

  1. RBAC:简单高效,适合传统应用
  2. ABAC:灵活强大,适合复杂场景
  3. 数据权限:细粒度控制,保障数据安全
  4. 统一接口:统一权限接口,便于使用

8.3 最佳实践

  1. 权限缓存:Redis缓存权限,提高性能
  2. 权限拦截:拦截器统一处理,减少代码侵入
  3. 数据权限:SQL拦截器自动过滤,透明实现
  4. 权限管理:权限配置界面,便于管理

相关文章