Skip to content
bitzorcas
中EN

Reference

对象存储与 S3 兼容集成:IFileStorage 安全实操

深度剖析 BitzOrcas.Modern 对象存储构建块,揭秘 IFileStorage 抽象、MinIO/AWS S3 生产适配器、客户端直传预签名 URL 与租户防越权隔离。

Last updated

在处理企业级文件上传(如发票附件、合同 PDF、报表导出)时,传统模式通常让客户端把大文件全部 POST 给 API 服务器,由服务端读入内存再转发到存储桶。 这种模式在高并发下存在致命隐患:

  1. API 网关被大文件打满:几十个几百兆的视频或大文件上传直接打爆应用服务器的带宽与内存;
  2. 凭据泄露风险:把 S3 的 Root AccessKey/SecretKey 暴露在前端客户端;
  3. 越权下载(IDOR):只要猜到文件的 Object Key,任何人都能随意下载其他租户的机密合同。

BitzOrcas.Modern 采用“预签名 URL 客户端直传 + 业务元数据绝对隔离”架构:服务端只签发带有短期凭据的 Presigned URL,客户端直接与 S3/MinIO 通信,服务端零带宽消耗。

预签名直传与下载安全全景

数据库 (FileAsset 实体)S3 / MinIO 对象存储API Host (业务鉴权)数据库 (FileAsset 实体)S3 / MinIO 对象存储API Host (业务鉴权)验证权限 -> 校验配额 -> 生成安全 ObjectKey浏览器前端1. POST /api/files/upload-session (申请上传,携带文件名与大小)12. 请求签发短效预签名上传 URL (PutObject Presigned URL)23. 返回签名 URL (5分钟有效)34. 返回 Presigned URL 与 SessionId45. PUT /bucket/object (直接将文件二进制流直传 S3,不走 API)56. HTTP 200 (上传成功)67. POST /api/files/upload-session/finalize (通知 API 完成落盘)78. 写入 FileAsset 实体 (绑定租户与权限)8浏览器前端

第一步:签发安全受控的预签名上传 URL

使用 IFileStorage 生成具有严格时间窗口与大小限制的上传地址:

CreateUploadSessionCommandHandler.cs: 签发预签名凭据
using BitzOrcas.Application.Abstractions.Storage;
using BitzOrcas.Domain.Results;
public sealed class CreateUploadSessionCommandHandler(
IFileStorage fileStorage,
ICurrentTenant currentTenant)
{
public async ValueTask<Result<UploadSessionDto>> Handle(
CreateUploadSessionCommand command,
CancellationToken ct)
{
// ① 生成防伪造的规范化 ObjectKey,包含租户隔离前缀与随机 GUID
var tenantId = currentTenant.Tenant.EffectiveTenantId;
var safeObjectKey = $"tenants/{tenantId}/documents/{Guid.NewGuid():N}/{command.FileName}";
// ② 签发短效预签名 PUT URL;单文件上限由 S3CompatibleStorageOptions.MaxSinglePutBytes 把守(默认 100MB)
var presignedUrl = await fileStorage.GeneratePresignedUploadUrlAsync(
objectKey: safeObjectKey,
ttl: TimeSpan.FromMinutes(15),
cancellationToken: ct);
return Result<UploadSessionDto>.Success(new UploadSessionDto(safeObjectKey, presignedUrl));
}
}

第二步:只读下载安全防盗链与鉴权

用户下载私有文件时,严禁向公网公开存储桶读权限。前端必须先通过 API 的业务授权,再由服务端动态签发 60 秒极短效的 GetObject 预签名地址:

GetFileDownloadUrlQueryHandler.cs: 动态签发安全下载链
public async ValueTask<Result<string>> Handle(GetFileDownloadUrlQuery query, CancellationToken ct)
{
// 1. 验证用户对该文件的业务访问权限(Files 模块防越权校验)
var fileResult = await fileRepository.FindAsync(query.FileId, ct);
if (fileResult.IsFailure)
{
return Result<string>.Failure(FileErrors.NotFound);
}
var fileAsset = fileResult.GetValueOrThrow();
// 2. 动态生成 60 秒有效的只读下载链接
var downloadUrl = await fileStorage.GetPresignedDownloadUrlAsync(new PresignedDownloadRequest(
ObjectKey: fileAsset.ObjectKey,
ExpiresIn: TimeSpan.FromSeconds(60)), ct);
return Result<string>.Success(downloadUrl);
}

第三步:环境适配器无缝切换

Provider(FileStorage:DefaultProvider)注册扩展适用场景
LocalAddBitzOrcasLocalStorage本地开发、无 Docker 离线测试
MinioAddBitzOrcasS3CompatibleStorage本地容器化联调与私有云部署——与 S3 走同一 S3CompatibleFileStore,不存在独立的 MinIOConnector
Unavailable默认注册的失败关闭端口未配置生产存储时的稳定失败语义

S3 兼容层同时覆盖 AWS S3 / 阿里云 OSS / 腾讯云 COS:连接段位于 FileStorage:S3。


总结

BitzOrcas 的对象存储设计实现了高性能与安全隔离:

  • API 零带宽占用:大文件直传直取 S3/MinIO;
  • 租户防盗链:私有桶 + 短效预签名确保文件不越权;
  • Provider 无缝切换:一套代码适配本地盘与主流云对象存储。

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%