396 lines
11 KiB
Markdown
396 lines
11 KiB
Markdown
# 统一资源权限控制使用说明
|
||
|
||
## 📋 概述
|
||
|
||
采用**统一权限表** `tb_resource_permission` 来管理所有资源(新闻、课程、任务等)的访问权限,支持基于**部门**和**角色**的细粒度权限控制。
|
||
|
||
## 🎯 设计优势
|
||
|
||
1. ✅ **统一管理**:所有资源类型的权限在一张表中管理
|
||
2. ✅ **灵活控制**:支持部门、角色、部门+角色三种授权方式
|
||
3. ✅ **细粒度**:支持读、写、执行三种权限级别
|
||
4. ✅ **易扩展**:新增资源类型只需添加类型编号
|
||
5. ✅ **高性能**:合理的索引设计,查询效率高
|
||
|
||
## 📊 表结构
|
||
|
||
```sql
|
||
tb_resource_permission (
|
||
id VARCHAR(50) 主键
|
||
resource_type INT(4) 资源类型
|
||
resource_id VARCHAR(50) 资源ID
|
||
dept_id VARCHAR(50) 部门ID(可为NULL)
|
||
role_id VARCHAR(50) 角色ID(可为NULL)
|
||
can_read TINYINT(1) 读权限
|
||
can_write TINYINT(1) 写权限
|
||
can_execute TINYINT(1) 执行权限
|
||
creator VARCHAR(50) 创建者
|
||
create_time TIMESTAMP 创建时间
|
||
...
|
||
)
|
||
```
|
||
|
||
## 🏷️ 资源类型编码
|
||
|
||
| 类型值 | 资源类型 | 说明 |
|
||
|--------|----------|------|
|
||
| 1 | 资源/新闻 | tb_resource |
|
||
| 2 | 课程 | tb_course |
|
||
| 3 | 课程章节 | tb_course_chapter |
|
||
| 4 | 学习任务 | tb_learning_task |
|
||
|
||
*可根据业务需要扩展更多类型*
|
||
|
||
## 🔐 权限组合说明
|
||
|
||
### 部门和角色组合
|
||
|
||
| dept_id | role_id | 含义 | 应用场景 |
|
||
|---------|---------|------|----------|
|
||
| NULL | NULL | 公开 | 所有人可访问 |
|
||
| 有值 | NULL | 部门限制 | 该部门所有人可访问 |
|
||
| NULL | 有值 | 角色限制 | 该角色所有人可访问(跨部门) |
|
||
| 有值 | 有值 | 部门+角色 | 特定部门的特定角色可访问 |
|
||
|
||
### 权限级别说明
|
||
|
||
| 权限字段 | 含义 | 典型操作 |
|
||
|---------|------|----------|
|
||
| can_read | 读权限 | 查看、浏览、阅读、下载 |
|
||
| can_write | 写权限 | 编辑、修改、删除 |
|
||
| can_execute | 执行权限 | 发布、审核、归档、推荐 |
|
||
|
||
## 💡 使用示例
|
||
|
||
### 示例1:公开资源
|
||
|
||
```sql
|
||
-- 所有人都可以阅读的公开资源
|
||
INSERT INTO tb_resource_permission (
|
||
id, resource_type, resource_id,
|
||
dept_id, role_id,
|
||
can_read, can_write, can_execute,
|
||
creator, create_time
|
||
) VALUES (
|
||
'1', 1, 'res_001',
|
||
NULL, NULL, -- 部门和角色都为NULL表示公开
|
||
1, 0, 0, -- 只有读权限
|
||
'admin', NOW()
|
||
);
|
||
```
|
||
|
||
### 示例2:部门限制
|
||
|
||
```sql
|
||
-- 只有"党委办公室"部门可以访问的资源
|
||
INSERT INTO tb_resource_permission (
|
||
id, resource_type, resource_id,
|
||
dept_id, role_id,
|
||
can_read, can_write, can_execute,
|
||
creator, create_time
|
||
) VALUES (
|
||
'2', 1, 'res_002',
|
||
'dept_party_office', NULL, -- 指定部门
|
||
1, 0, 0,
|
||
'admin', NOW()
|
||
);
|
||
```
|
||
|
||
### 示例3:角色限制(跨部门)
|
||
|
||
```sql
|
||
-- 所有"管理员"角色可以编辑的课程(不限部门)
|
||
INSERT INTO tb_resource_permission (
|
||
id, resource_type, resource_id,
|
||
dept_id, role_id,
|
||
can_read, can_write, can_execute,
|
||
creator, create_time
|
||
) VALUES (
|
||
'3', 2, 'course_001',
|
||
NULL, 'admin', -- 只指定角色
|
||
1, 1, 0, -- 读写权限
|
||
'admin', NOW()
|
||
);
|
||
```
|
||
|
||
### 示例4:部门+角色组合
|
||
|
||
```sql
|
||
-- "教务处"部门的"教师"角色可以管理的课程
|
||
INSERT INTO tb_resource_permission (
|
||
id, resource_type, resource_id,
|
||
dept_id, role_id,
|
||
can_read, can_write, can_execute,
|
||
creator, create_time
|
||
) VALUES (
|
||
'4', 2, 'course_002',
|
||
'dept_academic', 'teacher', -- 部门+角色
|
||
1, 1, 1, -- 全部权限
|
||
'admin', NOW()
|
||
);
|
||
```
|
||
|
||
### 示例5:多个授权组合
|
||
|
||
同一个资源可以有多条权限记录:
|
||
|
||
```sql
|
||
-- 示例:一个课程有多种访问权限
|
||
-- 1. 所有人可以查看
|
||
INSERT INTO tb_resource_permission VALUES
|
||
('5', 2, 'course_003', NULL, NULL, 1, 0, 0, 'admin', NOW());
|
||
|
||
-- 2. "教师"角色可以编辑
|
||
INSERT INTO tb_resource_permission VALUES
|
||
('6', 2, 'course_003', NULL, 'teacher', 1, 1, 0, 'admin', NOW());
|
||
|
||
-- 3. "教务处"的"管理员"可以发布
|
||
INSERT INTO tb_resource_permission VALUES
|
||
('7', 2, 'course_003', 'dept_academic', 'admin', 1, 1, 1, 'admin', NOW());
|
||
```
|
||
|
||
## 🔍 权限查询
|
||
|
||
### 查询1:检查用户对资源的访问权限
|
||
|
||
```sql
|
||
-- 检查用户是否可以访问某个资源
|
||
-- 假设:用户部门='dept_001',角色='teacher',资源类型=2,资源ID='course_001'
|
||
|
||
SELECT
|
||
MAX(can_read) as has_read,
|
||
MAX(can_write) as has_write,
|
||
MAX(can_execute) as has_execute
|
||
FROM tb_resource_permission
|
||
WHERE resource_type = 2
|
||
AND resource_id = 'course_001'
|
||
AND deleted = 0
|
||
AND (
|
||
-- 公开资源
|
||
(dept_id IS NULL AND role_id IS NULL)
|
||
OR
|
||
-- 部门匹配
|
||
(dept_id = 'dept_001' AND role_id IS NULL)
|
||
OR
|
||
-- 角色匹配
|
||
(dept_id IS NULL AND role_id = 'teacher')
|
||
OR
|
||
-- 部门+角色匹配
|
||
(dept_id = 'dept_001' AND role_id = 'teacher')
|
||
);
|
||
```
|
||
|
||
### 查询2:获取用户可访问的资源列表
|
||
|
||
```sql
|
||
-- 获取用户可以访问的所有课程
|
||
-- 假设:用户部门='dept_001',角色列表=['teacher', 'user']
|
||
|
||
SELECT DISTINCT rp.resource_id, c.*
|
||
FROM tb_resource_permission rp
|
||
INNER JOIN tb_course c ON c.course_id = rp.resource_id
|
||
WHERE rp.resource_type = 2
|
||
AND rp.can_read = 1
|
||
AND rp.deleted = 0
|
||
AND (
|
||
(rp.dept_id IS NULL AND rp.role_id IS NULL)
|
||
OR (rp.dept_id = 'dept_001' AND rp.role_id IS NULL)
|
||
OR (rp.dept_id IS NULL AND rp.role_id IN ('teacher', 'user'))
|
||
OR (rp.dept_id = 'dept_001' AND rp.role_id IN ('teacher', 'user'))
|
||
)
|
||
ORDER BY c.create_time DESC;
|
||
```
|
||
|
||
### 查询3:获取资源的所有权限配置
|
||
|
||
```sql
|
||
-- 查看某个资源配置了哪些权限
|
||
SELECT
|
||
rp.*,
|
||
d.name as dept_name,
|
||
r.name as role_name
|
||
FROM tb_resource_permission rp
|
||
LEFT JOIN tb_sys_dept d ON d.dept_id = rp.dept_id
|
||
LEFT JOIN tb_sys_role r ON r.role_id = rp.role_id
|
||
WHERE rp.resource_type = 1
|
||
AND rp.resource_id = 'res_001'
|
||
AND rp.deleted = 0;
|
||
```
|
||
|
||
## 🎨 业务场景实现
|
||
|
||
### 场景1:新闻资源的分级查看
|
||
|
||
```sql
|
||
-- 1. 公开新闻:所有人可查看
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 1, 'news_public', NULL, NULL, 1, 0, 0, 'admin', NOW());
|
||
|
||
-- 2. 内部新闻:全体员工可查看
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 1, 'news_internal', NULL, 'employee', 1, 0, 0, 'admin', NOW());
|
||
|
||
-- 3. 机密新闻:只有党委办公室可查看
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 1, 'news_confidential', 'dept_party', NULL, 1, 0, 0, 'admin', NOW());
|
||
```
|
||
|
||
### 场景2:课程权限分配
|
||
|
||
```sql
|
||
-- 1. 所有学生可以学习
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 2, 'course_101', NULL, 'student', 1, 0, 0, 'admin', NOW());
|
||
|
||
-- 2. 授课教师可以编辑
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 2, 'course_101', NULL, 'teacher', 1, 1, 0, 'admin', NOW());
|
||
|
||
-- 3. 教务处管理员可以发布
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 2, 'course_101', 'dept_academic', 'admin', 1, 1, 1, 'admin', NOW());
|
||
```
|
||
|
||
### 场景3:学习任务的部门分配
|
||
|
||
```sql
|
||
-- 为不同部门分配不同的学习任务
|
||
-- 党委办公室的任务
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 4, 'task_party', 'dept_party', NULL, 1, 0, 1, 'admin', NOW());
|
||
|
||
-- 教务处的任务
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 4, 'task_academic', 'dept_academic', NULL, 1, 0, 1, 'admin', NOW());
|
||
```
|
||
|
||
## 📝 Java代码示例
|
||
|
||
### 权限检查工具类
|
||
|
||
```java
|
||
public class ResourcePermissionChecker {
|
||
|
||
/**
|
||
* 检查用户是否有资源访问权限
|
||
*/
|
||
public boolean hasPermission(String userId, int resourceType,
|
||
String resourceId, PermissionType type) {
|
||
// 1. 获取用户的部门和角色
|
||
UserInfo user = getUserInfo(userId);
|
||
List<String> roleIds = user.getRoleIds();
|
||
String deptId = user.getDeptId();
|
||
|
||
// 2. 查询权限
|
||
List<ResourcePermission> permissions = permissionMapper.selectList(
|
||
new QueryWrapper<ResourcePermission>()
|
||
.eq("resource_type", resourceType)
|
||
.eq("resource_id", resourceId)
|
||
.eq("deleted", 0)
|
||
.and(wrapper -> wrapper
|
||
// 公开
|
||
.or(w -> w.isNull("dept_id").isNull("role_id"))
|
||
// 部门匹配
|
||
.or(w -> w.eq("dept_id", deptId).isNull("role_id"))
|
||
// 角色匹配
|
||
.or(w -> w.isNull("dept_id").in("role_id", roleIds))
|
||
// 部门+角色匹配
|
||
.or(w -> w.eq("dept_id", deptId).in("role_id", roleIds))
|
||
)
|
||
);
|
||
|
||
// 3. 判断权限
|
||
for (ResourcePermission perm : permissions) {
|
||
switch (type) {
|
||
case READ: if (perm.getCanRead()) return true; break;
|
||
case WRITE: if (perm.getCanWrite()) return true; break;
|
||
case EXECUTE: if (perm.getCanExecute()) return true; break;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 获取用户可访问的资源ID列表
|
||
*/
|
||
public List<String> getAccessibleResources(String userId, int resourceType) {
|
||
UserInfo user = getUserInfo(userId);
|
||
// ... 类似逻辑
|
||
return resourceIds;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 权限注解
|
||
|
||
```java
|
||
@Target({ElementType.METHOD})
|
||
@Retention(RetentionPolicy.RUNTIME)
|
||
public @interface RequirePermission {
|
||
ResourceType resourceType();
|
||
PermissionType permission() default PermissionType.READ;
|
||
}
|
||
|
||
// 使用示例
|
||
@RequirePermission(resourceType = ResourceType.COURSE, permission = PermissionType.WRITE)
|
||
public void updateCourse(String courseId, CourseDTO dto) {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
1. **NULL值处理**:dept_id 和 role_id 为 NULL 时有特殊含义,不能用空字符串代替
|
||
2. **权限优先级**:查询时使用 MAX() 聚合,任一条记录授权即有权限
|
||
3. **索引优化**:已创建组合索引 `idx_resource`,确保查询性能
|
||
4. **软删除**:使用 deleted 字段,避免物理删除导致的问题
|
||
5. **唯一约束**:`uk_resource_dept_role` 防止重复配置,包含 deleted 字段支持重复添加
|
||
|
||
## 🔄 权限变更场景
|
||
|
||
### 场景1:资源从公开改为部门限制
|
||
|
||
```sql
|
||
-- 1. 删除原公开权限
|
||
UPDATE tb_resource_permission
|
||
SET deleted = 1, delete_time = NOW()
|
||
WHERE resource_type = 1
|
||
AND resource_id = 'res_001'
|
||
AND dept_id IS NULL AND role_id IS NULL;
|
||
|
||
-- 2. 添加部门限制
|
||
INSERT INTO tb_resource_permission VALUES
|
||
(UUID(), 1, 'res_001', 'dept_target', NULL, 1, 0, 0, 'admin', NOW());
|
||
```
|
||
|
||
### 场景2:提升用户权限(从只读到读写)
|
||
|
||
```sql
|
||
-- 更新权限级别
|
||
UPDATE tb_resource_permission
|
||
SET can_write = 1,
|
||
updater = 'admin',
|
||
update_time = NOW()
|
||
WHERE resource_type = 2
|
||
AND resource_id = 'course_001'
|
||
AND role_id = 'teacher';
|
||
```
|
||
|
||
## 📈 性能优化建议
|
||
|
||
1. **合理使用索引**:已创建的索引包括 `idx_resource`、`idx_dept`、`idx_role`
|
||
2. **缓存策略**:对频繁访问的资源权限进行Redis缓存
|
||
3. **批量查询**:使用 IN 子句批量查询多个资源的权限
|
||
4. **避免全表扫描**:查询时务必带上 resource_type 和 resource_id
|
||
|
||
## 🔗 相关表说明
|
||
|
||
- `tb_sys_dept`: 部门表
|
||
- `tb_sys_role`: 角色表
|
||
- `tb_sys_user_dept_role`: 用户-部门-角色关联表
|
||
- `tb_resource`: 资源表
|
||
- `tb_course`: 课程表
|
||
- `tb_learning_task`: 学习任务表
|
||
|