Skip to content

文件密码与安全机制说明

一、设计目标概述

预览服务在处理 加密文件(加密 Office、PDF、压缩包、EPUB 等)时,引入了一套“基于 clientId 的密码解锁缓存机制”,目标是:

  • 避免用户在短时间内重复输入密码
  • 防止密码以明文形式落盘或长期存储
  • 在保证安全性的前提下,提升加密文件的预览体验

核心策略一句话概括:

密码只在校验通过后短暂存在于服务端缓存中,并且始终以加密形式存储,过期即失效。

二、核心配置:fileview.preview.security.password

yaml
fileview:
  preview:
    security:
      password:
        # 密码解锁状态 TTL(秒)
        ttlSeconds: 1800
        # 密码加密密钥(生产环境建议由 KMS / 配置中心注入)
        crypto:
          secret: preview-default-secret-key-change-in-production

配置含义说明

配置项说明默认值
ttlSeconds已解锁状态在缓存中的有效期(秒)1800(30 分钟)
crypto.secret用于派生 AES 密钥的应用级秘密默认内置

含义解释:

  • ttlSeconds
    • 同一个客户端(clientId)对同一个文件(fileId)成功解锁后,在 TTL 时间内再次访问,无需重新输入密码。
  • crypto.secret
    • 用于对用户输入的密码进行 AES-GCM 对称加密,确保密码在 Redis 中始终以密文形式存在。

注意

当前代码实际读取的配置前缀为 preview.security.password.*,如果只在 fileview.preview.security.* 下配置,运行时仍会使用默认值。
这是一个需要在生产部署前统一配置前缀的问题。

三、密码的安全存储策略

1. 密码不会明文存储

用户输入的密码在写入缓存前:

  • 使用 AES-256-GCM 算法加密
  • 每次加密都会生成随机 IV

Redis 中存储的始终是:

  • 加密后的密码,而非明文

2. 解锁状态与密码分离存储

在 Redis 中,针对同一个文件和客户端,会写入两类 key:

  • 解锁状态标记:
    • unlock:preview:{fileId}:{clientId}
  • 加密后的密码:
    • unlock:preview:pwd:{fileId}:{clientId}

两者具有相同的 TTL(由 ttlSeconds 控制):

  • TTL 到期后:
    • 解锁状态失效
    • 密码自动清除
    • 再次访问需要重新输入密码

四、对外解锁流程说明(接口级)

1. 密码解锁接口

POST /preview/api/password/unlock

解锁流程:

  1. 客户端提交:
    • 文件标识(fileId / originalFilePath
    • 密码
  2. 服务端校验密码是否正确:
    • 压缩包:验证是否可正常解压
    • Office / PDF:验证加密状态与密码合法性
  3. 校验成功后:
    • 写入「已解锁状态」
    • 写入「加密后的密码」
    • 设置 TTL(默认 30 分钟)

返回结果示例:

  • 密码正确:

    json
    {
      "valid": true,
      "passwordCorrect": true
    }
  • 密码错误:

    json
    {
      "valid": false,
      "passwordCorrect": false,
      "passwordRequired": true
    }

2. 解锁状态查询接口

GET /preview/api/password/status?fileId=xxx

根据 fileId + clientId 判断:

  • 是否存在有效的解锁状态

常用于:

  • 前端判断是否需要再次弹出密码输入框

五、密码机制在预览流程中的实际作用

1. 缓存命中 ≠ 一定可预览

即使:

  • 文件已经转换成功
  • 预览结果已存在缓存

但如果文件是加密文件,且当前 clientId 未在 TTL 内解锁:

  • 仍然会返回:PASSWORD_REQUIRED
  • 强制用户先完成密码解锁流程

✅ 这是为了防止「他人复用已缓存的加密文件预览结果」。

2. 已解锁文件的密码复用

在 TTL 有效期内:

  • 同一 clientId 再次预览同一文件:
    • 服务端可从缓存中恢复已验证的密码
    • 自动传递给转换 / 解压流程
    • 用户无需重复输入密码

适用场景包括:

  • 多次刷新页面
  • 多页压缩包 / EPUB 连续访问
  • 失败重试后再次预览

六、与缓存 / 长轮询机制的关系

  • 密码解锁状态本身是缓存的一部分
  • 长轮询过程中:
    • 每次检查转换结果时
    • 同时校验解锁状态是否仍然有效

若解锁 TTL 已过期:

  • 即使转换成功
  • 也会中断轮询并返回 PASSWORD_REQUIRED

七、安全性总结

这套文件密码机制具备以下特性:

  • ✅ 密码不明文存储
  • ✅ 密码自动过期
  • ✅ 解锁状态与客户端隔离(基于 clientId
  • ✅ 缓存结果无法绕过密码校验
  • ✅ 可与缓存、长轮询、转换流程无缝协作

八、一句话总结

预览服务通过基于 clientId 的密码解锁缓存机制,在保证密码安全的前提下,实现了加密文件“一次验证、短期复用、自动失效”的预览体验,有效平衡了安全性与易用性。