3. 归档包生命周期:创建、锁定、归档、解归档、删除各阶段的权限控制点
归档包从生成到销毁,经历五个核心阶段。每个阶段都对应独立的权限校验点,任何遗漏都可能导致数据泄露或运维事故。本章将逐一拆解各阶段的权限控制模型,并给出接口调用的标准校验逻辑。
3.1 生命周期全景:五阶段权限矩阵
下表列出了归档包在每个阶段可执行的操作、对应的权限点名称以及建议的校验时机。请将此表作为后续接口设计的基准参考。
| 阶段 | 核心操作 | 权限点标识 | 校验时机 | 默认授权范围 |
|---|---|---|---|---|
| 创建 | 初始化归档包元数据、上传内容 | archive:create |
POST 请求入口 | 项目管理员、归档操作员 |
| 锁定 | 禁止内容变更、设置不可变标记 | archive:lock |
锁定 API 调用前 | 仅项目管理员 |
| 归档 | 移入冷存储、更新状态为 archived | archive:archive |
状态转换前 | 项目管理员、合规审计员 |
| 解归档 | 从冷存储恢复、状态回退至 active | archive:unarchive |
解归档请求时 | 项目管理员 + 合规审批 |
| 删除 | 物理删除归档包及关联记录 | archive:delete |
DELETE 请求前 | 系统超级管理员 |
设计要点: 锁定与归档两个阶段必须严格分离。锁定仅负责“不可变”,归档负责“存储层级迁移”。如果允许未锁定的包直接归档,后续解归档时可能面临内容被篡改的合规风险。
3.2 创建阶段:准入校验与资源配额
创建归档包是生命周期的起点,权限控制集中在两个维度:操作者身份与资源配额。
- 身份校验: 调用
POST /api/v1/archives时,服务端必须校验当前用户是否拥有archive:create权限。建议使用 RBAC 模型,从 中提取角色列表,与项目级别的权限策略进行匹配。 - 配额校验: 在创建成功之前,检查项目或用户级别的归档包数量上限、总存储容量上限。超出配额时直接返回
403 QuotaExceeded,避免无效资源占用。 - 元数据预检: 归档包名称、标签、保留期限等字段必须通过服务端校验,防止恶意构造超长字段或非法字符。
// 创建阶段权限校验伪代码
function checkCreatePermission(user, project) {
if (!user.hasPermission("archive:create")) {
throw new ForbiddenException("缺少创建归档包权限");
}
if (project.archiveCount >= project.maxArchives) {
throw new QuotaExceededException("项目归档包数量已达上限");
}
if (project.archiveStorageUsed + estimatedSize > project.maxStorage) {
throw new QuotaExceededException("项目存储空间不足");
}
return true;
}
3.3 锁定阶段:不可变标记的权限红线
锁定操作将归档包标记为不可变,这是整个生命周期中最敏感的权限点。锁定后,任何内容修改、元数据编辑、甚至部分字段的更新都将被拒绝。
- 权限收紧: 仅项目管理员(
role:admin)可执行锁定操作。普通操作员即使拥有archive:create也无权锁定。 - 锁定前置条件: 归档包状态必须为
active,且不能已经处于锁定状态。锁定操作是幂等的,重复锁定应返回409 Conflict。 - 锁定记录: 每次锁定操作必须写入变更记录,包含操作者、时间戳、锁定原因。该记录将作为后续审计的依据。
常见陷阱: 不要在锁定后立即允许删除操作。锁定只保证内容不可变,不保证包不被删除。如果业务要求锁定后也不可删除,需要额外配置“保留策略”或“法定保留”权限点。
3.4 归档与解归档阶段:存储迁移的双向控制
归档(archive)和解归档(unarchive)是一对互逆操作,权限控制逻辑高度对称,但校验条件有显著差异。
| 对比维度 | 归档(archive) | 解归档(unarchive) |
|---|---|---|
| 权限点 | archive:archive |
archive:unarchive |
| 前置状态 | 必须为 locked |
必须为 archived |
| 额外审批 | 无需额外审批 | 需要合规审批 ticket |
| 存储影响 | 释放热存储,占用冷存储 | 释放冷存储,占用热存储 |
| 超时控制 | 通常即时完成 | 可能耗时较长,需异步回调 |
解归档阶段引入“合规审批”是为了防止冷数据被随意唤醒,增加运维成本或绕过数据保留策略。建议在解归档接口中增加 approval_ticket_id 字段,服务端在权限校验通过后,额外校验该 ticket 是否已审批通过。
// 解归档权限校验示例
function checkUnarchivePermission(user, archive, approvalTicketId) {
// 基础权限
if (!user.hasPermission("archive:unarchive")) {
throw new ForbiddenException("缺少解归档权限");
}
// 状态校验
if (archive.status !== "archived") {
throw new BadRequestException("归档包当前状态不允许解归档");
}
// 合规审批校验
if (!isTicketApproved(approvalTicketId)) {
throw new ForbiddenException("解归档需要合规审批通过");
}
return true;
}
3.5 删除阶段:最后的防线与软删除策略
删除操作是生命周期终点,权限控制最为严格。直接物理删除会永久丢失数据,因此推荐采用软删除 + 回收站机制。
- 权限隔离: 只有系统超级管理员(
role:superadmin)拥有archive:delete权限。项目管理员最多只能将归档包移入回收站。 - 软删除流程: 调用删除接口后,归档包状态变为
deleted,数据保留 30 天(可配置)。在保留期内,超级管理员可以执行恢复操作。 - 硬删除条件: 超过保留期后,系统自动触发物理删除。如果业务需要立即物理删除,必须二次确认并记录审计日志。
- 级联检查: 删除前必须检查归档包是否被其他资源引用(如变更记录、审查意见)。如果存在引用,应返回
409 Conflict并列出引用详情。
最佳实践: 在删除接口的响应体中返回
deleted_at 和 permanent_deletion_at 两个时间戳,让调用方明确知道数据何时会被彻底清除。这有助于前端展示“可恢复”倒计时。
3.6 生命周期权限控制全景图
下图展示了归档包从创建到删除的完整权限流转路径,以及每个阶段的关键校验节点。箭头方向代表状态转换方向,节点颜色区分权限等级。
3.7 接口调用中的权限校验顺序
在实际的 API 实现中,权限校验应遵循固定的顺序,避免因校验顺序不当导致的安全漏洞或性能浪费。推荐的校验流水线如下:
- 身份认证: 验证 有效性,提取用户 ID 和角色列表。
- 资源存在性: 检查归档包 ID 是否存在,不存在则直接返回 404,避免后续无意义的权限校验。
- 状态前置条件: 校验当前状态是否允许执行目标操作(例如,未锁定的包不能归档)。
- 权限点匹配: 校验用户是否拥有当前操作对应的权限点标识。
- 额外条件校验: 如配额、审批 ticket、引用检查等。
- 操作审计记录: 校验通过后,立即写入变更记录,确保可追溯。
// 通用权限校验流水线(伪代码)
function validateArchiveOperation(user, archiveId, operation, extraContext) {
// 1. 身份认证(由网关层完成,此处假设已通过)
// 2. 资源存在性
const archive = findArchiveOrThrow(archiveId);
// 3. 状态前置条件
checkStateTransition(archive.status, operation);
// 4. 权限点匹配
checkPermission(user, operation.permissionPoint);
// 5. 额外条件
checkExtraConditions(operation, extraContext);
// 6. 记录审计日志
writeAuditLog(user.id, archiveId, operation);
return archive;
}
性能提示: 将“资源存在性”校验放在“权限点匹配”之前,可以避免对不存在的资源进行复杂的权限计算,减少数据库查询次数。这在批量操作场景下效果尤为明显。