












一般业务系统的权限是"用户 → 角色 → 功能菜单"(RBAC)。档案系统在此基础上还有一层数据级权限——档案的可见范围是树状的:
全宗(一个单位/机构的所有档案)
└─ 门类(文书、科技、会计、人事、照片…)
└─ 案卷(一卷档案)
└─ 文件(卷内具体文件)

一个用户可能:能看 A 全宗文书档案的案卷级,但不能看 B 全宗;能看人事档案的文件级(因为他是人事科的),但看不到会计档案。权限要能作用到树的每一层,且层级间要能继承和覆盖。
权限 = 用户 × 操作(查看/下载/打印/编辑/删除) × 作用域(全宗/门类/案卷/文件)
四个层级:
| 层级 | 权限粒度 | 典型授予对象 | 说明 |
|---|---|---|---|
| 全宗级 | 能看该全宗全部档案 | 档案馆管理员 | 最高数据级授权 |
| 门类级 | 能看该门类全部档案 | 各科室(人事科看人事类) | 最常见的授权粒度 |
| 案卷级 | 能看指定案卷 | 项目组、借阅人 | 借阅授权通常到案卷 |
| 文件级 | 能看指定文件 | 单个申请 | 最细粒度,如某份合同 |
def can_access(user, archive_node, action):
"""判断用户对某个档案节点的访问权限"""
# 逐级向上找权限:文件 → 案卷 → 门类 → 全宗
node = archive_node
while node is not None:
perm = get_perm(user, node, action)
if perm == "deny":
return False # 显式禁止,立即拒绝
if perm == "allow":
return True # 显式允许
node = node.parent # 未设置,向上继承
return False # 全链路未授权,默认拒绝
默认拒绝(deny by default)是档案权限的铁律——没有授权就是不能访问,绝不默认放行。
| 方式 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| 数据库逐条存储权限 | 直观、可审计 | 权限记录量大(几千万档案逐条授权不现实) | 小体量、借阅授权 |
| 角色 + 规则表达式 | 灵活、存储小 | 规则复杂难排查 | 中体量 |
| 树节点授权 + 继承 | 兼顾粒度与存储量 | 实现稍复杂 | 生产推荐 |
第 19 篇提到"权限过滤进索引"——检索时也要做权限收敛:
用户检索 → 查出候选文档 → 按权限过滤(ES filter + 应用层校验)
双保险:ES 里用 permission_tag 粗筛(快),应用层再按树权限精校验(准)——防止绕过检索接口直接拼文档 ID 读取。
涉密档案(涉密单位的绝密/机密/秘密档案)权限要特殊处理:
坑 1:权限判断放在前端。 早期版本前端根据角色隐藏按钮,后端不校验——结果有人直接调 API 绕过。教训:所有权限判断必须在后端,前端只是体验优化,不是安全边界。
坑 2:借阅到期权限不回收。 借阅授权到期后没自动回收,读者一直能看。解法:授权带有效期 + 定时任务到期回收 + 到期提醒(到期前 3 天提醒续借或归档)。
坑 3:权限变更不通知。 有人被撤权后,会话里还残留旧权限缓存。解法:权限变更 → 缓存失效 → 强制重新加载;敏感权限变更(撤权)立即踢下线重登。
坑 4:导出环节权限绕过。 用户能看某些档案,但系统"批量导出"功能把整卷导出(包含他没权限的文件)。解法:导出前按单文件权限过滤,无权限的文件跳过并在导出清单里标注。
权限模型是档案系统"专业性"的直接体现,也是项目验收重点:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。