11 KiB
11 KiB
统一资源权限控制使用说明
📋 概述
采用统一权限表 tb_resource_permission 来管理所有资源(新闻、课程、任务等)的访问权限,支持基于部门和角色的细粒度权限控制。
🎯 设计优势
- ✅ 统一管理:所有资源类型的权限在一张表中管理
- ✅ 灵活控制:支持部门、角色、部门+角色三种授权方式
- ✅ 细粒度:支持读、写、执行三种权限级别
- ✅ 易扩展:新增资源类型只需添加类型编号
- ✅ 高性能:合理的索引设计,查询效率高
📊 表结构
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:公开资源
-- 所有人都可以阅读的公开资源
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:部门限制
-- 只有"党委办公室"部门可以访问的资源
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:角色限制(跨部门)
-- 所有"管理员"角色可以编辑的课程(不限部门)
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:部门+角色组合
-- "教务处"部门的"教师"角色可以管理的课程
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:多个授权组合
同一个资源可以有多条权限记录:
-- 示例:一个课程有多种访问权限
-- 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:检查用户对资源的访问权限
-- 检查用户是否可以访问某个资源
-- 假设:用户部门='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:获取用户可访问的资源列表
-- 获取用户可以访问的所有课程
-- 假设:用户部门='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:获取资源的所有权限配置
-- 查看某个资源配置了哪些权限
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:新闻资源的分级查看
-- 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:课程权限分配
-- 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:学习任务的部门分配
-- 为不同部门分配不同的学习任务
-- 党委办公室的任务
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代码示例
权限检查工具类
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;
}
}
权限注解
@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) {
// ...
}
⚠️ 注意事项
- NULL值处理:dept_id 和 role_id 为 NULL 时有特殊含义,不能用空字符串代替
- 权限优先级:查询时使用 MAX() 聚合,任一条记录授权即有权限
- 索引优化:已创建组合索引
idx_resource,确保查询性能 - 软删除:使用 deleted 字段,避免物理删除导致的问题
- 唯一约束:
uk_resource_dept_role防止重复配置,包含 deleted 字段支持重复添加
🔄 权限变更场景
场景1:资源从公开改为部门限制
-- 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:提升用户权限(从只读到读写)
-- 更新权限级别
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';
📈 性能优化建议
- 合理使用索引:已创建的索引包括
idx_resource、idx_dept、idx_role - 缓存策略:对频繁访问的资源权限进行Redis缓存
- 批量查询:使用 IN 子句批量查询多个资源的权限
- 避免全表扫描:查询时务必带上 resource_type 和 resource_id
🔗 相关表说明
tb_sys_dept: 部门表tb_sys_role: 角色表tb_sys_user_dept_role: 用户-部门-角色关联表tb_resource: 资源表tb_course: 课程表tb_learning_task: 学习任务表