Files
schoolNews/schoolNewsServ/.bin/mysql/sql/权限控制使用说明.md
2025-10-15 10:39:51 +08:00

11 KiB
Raw Permalink Blame History

统一资源权限控制使用说明

📋 概述

采用统一权限表 tb_resource_permission 来管理所有资源(新闻、课程、任务等)的访问权限,支持基于部门角色的细粒度权限控制。

🎯 设计优势

  1. 统一管理:所有资源类型的权限在一张表中管理
  2. 灵活控制:支持部门、角色、部门+角色三种授权方式
  3. 细粒度:支持读、写、执行三种权限级别
  4. 易扩展:新增资源类型只需添加类型编号
  5. 高性能:合理的索引设计,查询效率高

📊 表结构

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) {
    // ...
}

⚠️ 注意事项

  1. NULL值处理dept_id 和 role_id 为 NULL 时有特殊含义,不能用空字符串代替
  2. 权限优先级:查询时使用 MAX() 聚合,任一条记录授权即有权限
  3. 索引优化:已创建组合索引 idx_resource,确保查询性能
  4. 软删除:使用 deleted 字段,避免物理删除导致的问题
  5. 唯一约束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';

📈 性能优化建议

  1. 合理使用索引:已创建的索引包括 idx_resourceidx_deptidx_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: 学习任务表