跳到主内容

如何在 Casdoor 中使用 CDN 托管静态文件:S3、MinIO、Azure Blob 及其他存储提供商

· 阅读时间:一分钟
Yang Luo
Casdoor Maintainer

要在 CDN 上托管静态文件,请将它们放入对象存储(Amazon S3、MinIO、Azure Blob Storage、Google Cloud Storage、Alibaba Cloud OSS 或 Tencent Cloud COS),让 CDN 以该存储桶作为源站拉取内容,并提供基于 CDN 域名的 URL。在 Casdoor 中,这只是一个设置项:为存储桶创建一个存储提供商,并在其 Domain 字段中填入 CDN 地址。Casdoor 通过提供商的 API 上传头像和其他文件,并保存以该域名开头的 URL。Casdoor 自身的前端也可以通过 app.conf 中的 frontendCdnUrl 和 staticBaseUrl 从 CDN 加载。

存储提供商概览​

存储提供商决定了 Casdoor 将上传的文件写入何处:用户头像、身份证图片、服务条款页面,以及在 Resources 页面上传的任何文件。你将提供商添加到应用中后,该应用中的上传操作就会使用该提供商。存储提供商概述列出了所有类型。下表涵盖了人们通常询问的类型:

存储Casdoor 提供商类型Casdoor 是否使用 S3 API?可自托管公共 URL 如何设置
本地文件系统Local File System否,磁盘上的普通文件是,即 Casdoor 服务器由 Casdoor 在 /files/ 下提供服务。Domain 为只读
Amazon S3AWS S3是否Domain:存储桶 URL 或 CloudFront 域名。Endpoint (Intranet):VPC 端点
MinIOMinIO是,使用路径风格请求是Domain:MinIO URL 后跟存储桶名称,或 CDN 域名
Alibaba Cloud OSSAliyun OSS否,使用 OSS SDK否Domain:CDN 域名。若留空,则变为 bucket.endpoint。Endpoint (Intranet):内部端点
Tencent Cloud COSTencent Cloud COS否,使用 COS API否Domain:CDN 域名
Azure Blob StorageAzure Blob否,使用 Blob API否Domain:自定义域名或 Front Door 域名。Casdoor 会在路径中添加容器名称
Google Cloud StorageGoogle Cloud Storage否,使用 Google Go 客户端否Domain:若留空,则变为 https://storage.googleapis.com。Casdoor 会在路径中添加存储桶名称
Synology NASSynology否,使用 DSM File Station API是无 Domain 字段。URL 基于 Endpoint

Casdoor 还支持 Qiniu Cloud Kodo、CUCloud OSS 以及 Casdoor(后者将文件存储在另一个 Casdoor 实例中)。针对 Amazon S3、MinIO、Azure Blob、Google Cloud Storage、Alibaba Cloud OSS、Tencent Cloud COS、Synology NAS 和 本地文件系统 的各提供商指南展示了每个字段的取值。

Endpoint 与 Domain 的区别​

这两个字段的作用不同,大多数 CDN 问题都源于混淆了它们。

  • Endpoint 是 Casdoor 用于访问存储 API 的地址:上传、删除、列表查询。CDN 不涉及此过程。
  • 当在 app.conf 中设置 isCloudIntranet = true 时,Endpoint (Intranet) 会替代 Endpoint。这使上传流量保持在提供商的内部网络中,适用于 Casdoor 运行在同一云环境中的情况。该字段适用于 AWS S3、Aliyun OSS、Azure Blob 和 CUCloud OSS。
  • Domain 是 Casdoor 保存的 URL 以及浏览器请求的 URL 的基础部分。如果您有 CDN,请在此处填写其域名。
  • Path prefix 会添加到每个对象键的前面。如果前缀为 casdoor,则头像最终将位于 casdoor/avatar/... 下。

Casdoor 根据 Domain 的协议和主机名构建文件 URL,然后是 Domain 的路径,接着是路径前缀,最后是文件路径。如果没有协议,Domain 会自动添加 https://。Domain 的路径部分也会成为对象键的一部分,因此 https://cdn.example.com/casdoor/ 会将文件存储在存储桶的 casdoor/ 目录下。对于 Azure Blob 和 Google Cloud Storage,Casdoor 会在主机名后添加容器或存储桶名称作为第一个路径段。您的 CDN 需要能够服务该路径。

如果 Domain 为空,Casdoor 会在首次使用提供商时从提供商的端点填充它并保存。有时结果可行,例如 OSS 和 GCS。有时则不行:对于不包含存储桶名称的 S3 端点,或者对于 MinIO(其中路径样式请求将存储桶名称放在路径中),生成的 URL 会缺少存储桶名称。请自行设置 Domain,上传一个测试头像,并在其他人使用之前打开 Casdoor 返回的 URL。

Casdoor 用于社交登录复制头像的提供商来自 app.conf 中的 defaultStorageProvider。它是属于 admin 的提供商的名称(参见 配置参考):

defaultStorageProvider = provider_storage_s3
isCloudIntranet = false

公开存储桶、私有存储桶和签名 URL​

Casdoor 在用户的 avatar 字段和资源记录中保存一个普通的永久 URL。它不会为这些文件创建签名或过期 URL。因此,Domain 处的内容必须能够响应匿名 GET 请求。有两种方法可以实现这一点。

将对象设为公开。 对于 AWS S3 和 MinIO,Casdoor 使用 public-read 预设 ACL 上传每个对象。对于 Aliyun OSS,客户端库默认执行相同操作。在 S3 上,这与新存储桶的默认设置冲突:根据 AWS 的说法,新存储桶使用 Bucket owner enforced 设置,这会禁用 ACL,而设置 public-read 等 ACL 的上传会因 AccessControlListNotSupported 而失败。这就是为什么 Amazon S3 指南 要求您启用 ACL 并关闭 Block all public access。在 OSS 上,对象 ACL 优先于存储桶 ACL,因此即使在私有存储桶中,public-read 对象也可以被读取。MinIO 不使用 ACL 进行匿名读取,而是使用存储桶策略:

mc anonymous set download myminio/casdoor

Azure 需要在存储账户上允许匿名访问,并将容器设置为 Blob 访问级别。Google Cloud Storage 需要在存储桶上将 Storage Object Viewer 角色 (roles/storage.objectViewer) 授予 allUsers。

保持存储桶私有,仅让 CDN 读取它。 CDN 对其到源站的请求进行签名,而查看者通过 CDN 访问时无需签名。每个供应商都有自己的版本:

  • CloudFront 具有源站访问控制 (OAC)。AWS 要求 OAC 使用 Bucket owner enforced,如果您需要 ACL,则使用 Bucket owner preferred。
  • 阿里云 CDN 在您授权后可以从未公开的 OSS 存储桶中获取数据。
  • 腾讯云 CDN 对私有 COS 存储桶具有回源鉴权功能。
  • Azure Front Door Premium 可以通过专用链接连接到存储账户。
  • Google 的 Cloud CDN 指南也涵盖了私有存储桶访问,其中 Storage Object Viewer 角色授予服务账户而不是 allUsers。

这隐藏了存储桶自身的 URL,但文件仍可通过 CDN 公开访问。对于 AWS S3 和 Aliyun OSS 类型,由于 Casdoor 设置了 public-read ACL,对象在其存储桶 URL 处也保持可读。

如果头像完全不能公开,则在对象存储前放置 CDN 是错误的工具。将这些文件保留在您控制的存储类型上,并限制谁能访问该主机。

缓存标头和头像陷阱​

在 S3 和 MinIO 上,Casdoor 在上传时根据文件扩展名设置 Content-Type。它在任何提供商上都不设置 Cache-Control。这意味着应用 CDN 的默认 TTL。在 CloudFront 上,没有 Cache-Control 的对象会被缓存默认的 TTL,如果不使用缓存策略,则为 24 小时。要按对象控制此行为,请在存储桶中添加 Cache-Control 元数据。对于整个路径,请在 CDN 上使用缓存规则。

头像需要较短的 TTL。当用户更改其图片时,Casdoor 将新文件写入相同的键 avatar/<organization>/<username>.<extension>,并保存相同的 URL。较长的 TTL 意味着人们会看到旧图片,直到其过期或您清除或失效该路径。其他上传在名称已被占用时会获得编号后缀 (-1, -2, ...),因此新上传会获得新的 URL,并且可以长时间缓存。

一种有效的布局:

路径建议缓存
avatar/*短 TTL(分钟),或在更改时清除
resource/* 和其他上传长 TTL
哈希前端资源 (assets/*)非常长的 TTL。文件名在每次构建时都会更改

CDN 上的 Casdoor 自身前端​

除了用户上传之外,还有三种方法可以将 Casdoor 自身的静态文件移出 Casdoor 服务器。

frontendCdnUrl。 如果您从 casdoor-web npm 包安装前端,请使用此项。将其指向 npm CDN,例如:

frontendCdnUrl = "https://cdn.jsdelivr.net/npm/{name}@{version}"

Casdoor 从构建文件夹中的 package.json 读取包名和版本。它将 index.html 中的脚本和样式表链接重写为该 CDN,并将本地路径保留为后备方案。如果文件加载失败,浏览器会从 Casdoor 加载它,并且 cookie 会使该浏览器在会话剩余时间内继续使用本地文件。您自己构建的前端没有这样的 package.json,因此该设置对其无效。

部署脚本。 对于从源码构建的前端,将前端文件托管在 CDN 上解释了 deployment/deploy_test.go。它通过你的某个存储提供商上传 web/build/assets/,并将 index.html 指向该提供商的 Domain(必须以 / 结尾)。Vite 会对这些文件名进行哈希处理,因此它们适合上表中的长 TTL。

staticBaseUrl。 此设置指定默认图片(如徽标、提供商图标和默认头像)的来源。默认值为 https://cdn.casbin.org,它提供 casbin/static 仓库的内容。在没有互联网访问权限的网络中,你可以自行托管该仓库的副本,并将 staticBaseUrl 指向它,如在内网托管静态文件中所述。

选择一种方案​

  • 单服务器、小规模用户群: Local File System。如果 Casdoor 运行在容器中,请将 files 目录挂载到卷上。Casdoor 会向这些文件发送 X-Content-Type-Options: nosniff 和一个沙箱化的 Content-Security-Policy,因此上传的 HTML 或 SVG 文件无法在你的 Casdoor 域名上执行脚本。
  • 自托管且兼容 S3: MinIO。文档也建议其他兼容 S3 的存储使用此类型。
  • 已在云端: 使用该云的对象存储和 CDN,在 Casdoor 运行的同一云中设置 Endpoint (Intranet),并将 Domain 设置为 CDN。

参考资料​