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 生命周期权限控制全景图

下图展示了归档包从创建到删除的完整权限流转路径,以及每个阶段的关键校验节点。箭头方向代表状态转换方向,节点颜色区分权限等级。

归档包生命周期权限控制全景图 归档包生命周期权限控制全景图 创建 archive:create 锁定 archive:lock 归档 archive:archive 解归档 archive:unarchive 删除 archive:delete 状态: active 状态: locked 状态: archived 恢复至 active 仅超级管理员 权限等级说明: 蓝色 = 基础操作权限(操作员及以上) 红色/紫色 = 敏感操作权限(管理员及以上) 橙色 = 高成本操作权限(管理员 + 合规审批) 青色 = 销毁操作权限(仅超级管理员)

3.7 接口调用中的权限校验顺序

在实际的 API 实现中,权限校验应遵循固定的顺序,避免因校验顺序不当导致的安全漏洞或性能浪费。推荐的校验流水线如下:

  1. 身份认证: 验证 有效性,提取用户 ID 和角色列表。
  2. 资源存在性: 检查归档包 ID 是否存在,不存在则直接返回 404,避免后续无意义的权限校验。
  3. 状态前置条件: 校验当前状态是否允许执行目标操作(例如,未锁定的包不能归档)。
  4. 权限点匹配: 校验用户是否拥有当前操作对应的权限点标识。
  5. 额外条件校验: 如配额、审批 ticket、引用检查等。
  6. 操作审计记录: 校验通过后,立即写入变更记录,确保可追溯。
// 通用权限校验流水线(伪代码)
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;
}
性能提示: 将“资源存在性”校验放在“权限点匹配”之前,可以避免对不存在的资源进行复杂的权限计算,减少数据库查询次数。这在批量操作场景下效果尤为明显。