Files
schoolNews/schoolNewsServ/.bin/mysql/sql/权限控制使用说明.md

396 lines
11 KiB
Markdown
Raw Permalink Normal View History

2025-10-15 10:39:51 +08:00
# 统一资源权限控制使用说明
## 📋 概述
采用**统一权限表** `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`: 学习任务表