forked from wangziqi/gongxue-base
feat: harden content asset access
This commit is contained in:
@@ -52,6 +52,12 @@ GET /api/catalog/assets/preview?assetId=...
|
||||
POST /api/videos/play
|
||||
```
|
||||
|
||||
后台查看资源访问事件:
|
||||
|
||||
```text
|
||||
GET /api/tenant-content/assets/access-events?assetId=...
|
||||
```
|
||||
|
||||
后台管理员下载:
|
||||
|
||||
```text
|
||||
@@ -86,8 +92,9 @@ npm --workspace @tiku-saas/worker run exports:once
|
||||
4. 调用 `POST /api/tenant-content/assets/confirm-upload`,由后端读取对象元数据并比对大小、MIME、SHA-256。
|
||||
5. 校验通过且 `publish=true` 时,后端将资源置为 `status=active`、`uploadStatus=verified`。
|
||||
6. 学生端只能下载或预览 `active + verified` 的托管对象资源。
|
||||
7. 生产环境定时运行 assets worker,复检 `pending/verified` 托管对象的大小、MIME、SHA-256 等元数据。
|
||||
8. 如果复检发现对象丢失、跨租户 objectKey、大小/MIME/checksum 不一致,worker 会把资源置为 `uploadStatus=failed`,并将 `active` 资源退回 `draft`,同时写入 `security_flags.assetRecheckFailed=true` 和 `audit_logs`。
|
||||
7. 学生端和后台管理员下载/预览都会写入 `content_asset_access_events`,包含 `assetId/userId/accessType/result/expiresInSec/signatureMode/ip/userAgent` 等审计字段。
|
||||
8. 生产环境定时运行 assets worker,复检 `pending/verified` 托管对象的大小、MIME、SHA-256 等元数据。
|
||||
9. 如果复检发现对象丢失、跨租户 objectKey、大小/MIME/checksum 不一致,worker 会把资源置为 `uploadStatus=failed`,并将 `active` 资源退回 `draft`,同时写入 `security_flags.assetRecheckFailed=true` 和 `audit_logs`。
|
||||
|
||||
## 题库导出生成资源
|
||||
|
||||
@@ -131,11 +138,45 @@ npm --workspace @tiku-saas/worker run exports:once
|
||||
- 上传会校验 MIME 类型和文件大小。
|
||||
- 托管对象资源未确认前不能发布为 `active`,学生端不可下载。
|
||||
- PDF/图片预览使用 `inline` 签名,不等同于长期公开 URL。
|
||||
- 下载和视频播放必须先经过 API 权限判断,再下发短期签名 URL。
|
||||
- 下载、预览和视频播放必须先经过 API 权限判断,再下发短期签名 URL。
|
||||
- 学生端 `preview`、锁定资料下载、视频和资料包默认使用更短 TTL。当前策略是学生 inline 预览、`members/svip/private` 资料、`video/package` 资源最多 300 秒;租户后台预览最多 3600 秒,后台下载最多 86400 秒。
|
||||
- `visibility in ('members','svip','private')` 的外部 CDN/直链资源默认会被拒绝,除非资源 `metadata.providerManagedAccess=true` 或 `metadata.cdnAccessMode='signed_by_provider'`。商用建议这类资源优先登记为 `objectKey`,由 API 生成 OSS/COS/Supabase Storage 私有签名 URL。
|
||||
- 每次上传签名、上传确认、学生下载/预览、后台下载/预览都会写入 `content_asset_access_events`。授权失败也会记录 `result=denied` 和 `denyCode`,用于租户后台排查资源访问问题。
|
||||
- 云厂商 AccessKey、SecretKey、Service Role Key 只存在服务端环境变量,不返回前端。
|
||||
- `content_assets` 是资源唯一台账,前端不得绕过台账直接访问私有 bucket。
|
||||
- 前端不能把 `uploadStatus=failed` 或 `status=draft` 的资源继续展示为可下载;列表仍返回时应展示“资料处理中”或“资源异常已下架”,真正下载/预览会被后端拒绝。
|
||||
|
||||
## 访问事件字段
|
||||
|
||||
资源访问事件表:
|
||||
|
||||
```text
|
||||
public.content_asset_access_events
|
||||
```
|
||||
|
||||
关键字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `tenant_id` | 租户隔离维度 |
|
||||
| `asset_id` | 资源 ID,上传签名阶段可能为空 |
|
||||
| `user_id` | 学生或后台操作者 |
|
||||
| `actor_role` | `anonymous/student/tenant_content_editor/tenant_admin/system` |
|
||||
| `access_type` | `download/preview/admin_download/admin_preview/upload_sign/upload_confirm` |
|
||||
| `result` | `granted` 或 `denied` |
|
||||
| `deny_code` | 拒绝原因,例如 `ASSET_CDN_ACCESS_NOT_ALLOWED` |
|
||||
| `expires_in_sec` | 下发签名有效期 |
|
||||
| `signature_mode` | `local-placeholder`、`supabase-storage-signed-url`、`aliyun-oss-signature-url-v1`、`tencent-cos-signature-url-v5`、`public-or-provider-managed` |
|
||||
| `metadata` | 签名摘要、对象位置、文件名等排查信息;不保存云厂商密钥 |
|
||||
|
||||
后台接口:
|
||||
|
||||
```text
|
||||
GET /api/tenant-content/assets/access-events?assetId=<assetId>&limit=100
|
||||
```
|
||||
|
||||
前端只用于后台审计和排查,不要把 `content_asset_access_events` 当成学生端下载列表来源。
|
||||
|
||||
## 环境变量
|
||||
|
||||
通用:
|
||||
@@ -198,6 +239,6 @@ WORKER_ASSET_REQUEST_TIMEOUT_MS=10000
|
||||
|
||||
## 官方依据
|
||||
|
||||
- 阿里云 OSS Node.js SDK 支持通过 `signatureUrl` 为上传/下载生成带过期时间的签名 URL,并可通过对象 HEAD 读取元数据。
|
||||
- 腾讯云 COS XML API V5 签名由 `q-sign-algorithm`、`q-ak`、`q-sign-time`、`q-key-time`、`q-header-list`、`q-url-param-list`、`q-signature` 等字段组成,可用于预签名 URL 和 HEAD Object。
|
||||
- Supabase Storage 提供 `createSignedUploadUrl` 和 `createSignedUrl`,分别用于签名上传和签名下载;私有 bucket 仍应配合 RLS、服务端权限控制和资源台账。
|
||||
- Supabase Storage JavaScript `createSignedUrl` 用于为文件创建固定有效期的签名 URL,并要求对象具备 `select` 权限;本项目由服务端集中处理权限与签名。
|
||||
- 阿里云 OSS 官方文档建议服务端生成 signed URL 后让客户端直传对象,也说明私有对象可通过 presigned URL 在有效期内授权下载或预览。
|
||||
- 腾讯云 COS 文档给出的签名 URL 格式包含 `q-sign-algorithm`、`q-ak`、`q-sign-time`、`q-key-time`、`q-header-list`、`q-url-param-list`、`q-signature` 等字段;本项目服务端生成签名,不把 SecretKey 下发前端。
|
||||
|
||||
Reference in New Issue
Block a user