七牛云私有空间存储机密文件,及使用 golang 生成带有凭证的下载链接

更新日期: 2024-09-12 阅读次数: 169 字数: 1224 分类: CDN

为何使用私有空间

主要是公司内部系统的业务(质量管理系统),需要上传一些内部资料。 我不是很确实是否是保密信息,但是感觉上是不应该能够公开访问。 这不像是网站静态资源,例如网站图片,或者 js,css 等,上传就是为了公开能访问。

所以决定尝试一下七牛云存储的私有空间。(当然,阿里云也有类似的 OSS 存储服务。)

增加一个 bucket 空间

旧有的空间是开放空间,需要新建一个私有空间。

七牛云私有空间

但是新增一个域名就麻烦了。。。

  • 配置 CDN 域名
  • 配置 HTTPS 证书

现在 七牛云 HTTPS 证书 只能配置3个月的,3个月后还需要手动重新配置。(去年还是可以设置一年的,今年就变成了 3 个月)

空间命名

因为目前公司内部的项目,又细分为不同子公司,不同的项目。为了省事,就使用同一个 CDN 域名,毕竟 3 个月手动换一次证书不是开玩笑的。

最好是区分好目录:

<子公司名缩写>/<项目名缩写>/<功能名缩写>/文件名

私有空间的原理

参考,七牛官方文档:

https://developer.qiniu.com/kodo/1656/download-private

下载链接需要附加参数 e 和 token。链接格式:

http://<domain>/<key>?e=<deadline>&token=<downloadToken>
  • e 表示 URL 的过期时间,采用Unix时间戳,单位为秒。超时的访问将返回 401 错误。
  • 参数 token 表示下载凭证。估计是对 e 的加密后的信息。下载凭证是对资源访问的授权,不带下载凭证或下载凭证不合法都会导致 401 错误,表示验证失败。

注意:token 参数需要放到 URL 的最后,其后面的参数将被忽略。

下载凭证的生成原理

参考:

https://developer.qiniu.com/kodo/1202/download-token

  1. 原始下载链接
  2. 下载链接加上 e 参数,即时间戳
  3. 对带时间戳的 URL 做 SHA1 计算,然后取 Base64 编码
  4. 拼接成 token,附加在 URL 最后。token 分为两部分,用冒号分隔。冒号前面是加密用的 key (官方 SDK 中默认使用的是 access key),冒号后是加密生成的串的 base64 编码。

一个真实文件的链接格式:

https://cdn.sunzhongwei.com/game/red-alert/test/image.jpg?e=1726101702&token=xxxxx:op7zkku4pLI-oG1KG6jf6hB9VGM=

这个文件链接,是通过手动上传文件到私有空间,然后在后台点击这个文件的菜单,选择“复制签名链接”获取到的。 默认的有效时间是 300 秒,即 5 分钟。

七牛云私有空间带有凭证的下载链接

golang 相关接口

官方 Go SDK 的文档,真是一言难尽噶,看不懂:

https://developer.qiniu.com/kodo/1238/go#6

import (
    "context"
    "time"
    "github.com/qiniu/go-sdk/v7/storagev2/credentials"
    "github.com/qiniu/go-sdk/v7/storagev2/downloader"
)

accessKey := "your access key"
secretKey := "your secret key"
mac := credentials.NewCredentials(accessKey, secretKey)

domain := "download.domain.com"
urlsProvider := downloader.SignURLsProvider(downloader.NewStaticDomainBasedURLsProvider([]string{domain}), downloader.NewCredentialsSigner(mac), &downloader.SignOptions{
    TTL: 1 * time.Hour, // 有效期
})

需要结合 github 上的代码来看。

https://github.com/qiniu/go-sdk/blob/master/storagev2/downloader/signers.go

很奇怪这里用的都是 storagev2 的接口,跟 storage 的区别是什么呢?从 github 代码来看,storage 中也会使用 storagev2 中的 downloader 相关的接口。 我看 upload token 也是用这个 storagev2 接口生成的。

需要本地用实际代码测试一下。例如,手动在七牛后台上传,然后用 sdk 生成下载链接,测试是否能够正确下载,过期后是否会禁止下载。

可用代码

还是得靠 AI。。。

看了一下午 sdk 的源码,最后被 AI 一秒钟解决了。。。。

https://github.com/qiniu/go-sdk/blob/master/storage/bucket.go#L1275

对应的 sdk 代码:

使用里面的 MakePrivateURLv2 即可。

// MakePrivateURL 用来生成私有空间资源下载链接,注意该方法并不会对 key 进行 escape
func MakePrivateURL(mac *auth.Credentials, domain, key string, deadline int64) (privateURL string) {
	publicURL := MakePublicURL(domain, key)
	urlToSign := publicURL
	if strings.Contains(publicURL, "?") {
		urlToSign = fmt.Sprintf("%s&e=%d", urlToSign, deadline)
	} else {
		urlToSign = fmt.Sprintf("%s?e=%d", urlToSign, deadline)
	}
	token := mac.Sign([]byte(urlToSign))
	privateURL = fmt.Sprintf("%s&token=%s", urlToSign, token)
	return
}

// MakePrivateURLv2 用来生成私有空间资源下载链接,并且该方法确保 key 将会被 escape
func MakePrivateURLv2(mac *auth.Credentials, domain, key string, deadline int64) (privateURL string) {
	return MakePrivateURLv2WithQuery(mac, domain, key, nil, deadline)
}

// MakePublicURL 用来生成公开空间资源下载链接,注意该方法并不会对 key 进行 escape
func MakePublicURL(domain, key string) (finalUrl string) {
	domain = strings.TrimRight(domain, "/")
	srcUrl := fmt.Sprintf("%s/%s", domain, key)
	srcUri, _ := url.Parse(srcUrl)
	finalUrl = srcUri.String()
	return
}

例如生成一个有效期为 1 个小时的下载链接:

mac := qbox.NewMac(qiniu_access_key, qiniu_secret_key)
url := storage.MakePrivateURLv2(
		mac, qiniu_url_prefix_private, key,
		time.Now().Add(time.Hour*1).Unix())

手动测试

  • 手动上传一个文件到私有空间。记录这个文件的 key, 即,文件路径,tx/qms/test/GWjt0QqWIAAiszg.jpg
  • 在 golang 项目的配置文件中,新增一个私有空间的相关配置。但是我从七牛后台看,多个空间,可以使用相同的 AK/SK (Access Key / Secret Key)。

error: download token auth failed

但是我生成的 token,冒号前的部分为空。如:

image.jpg?e=1726107479&token=:PdHKk8Gg-yTofbUwDwlpLb6xbL8=

且没有域名。原来是单元测试代码,没有加载 .env 里的七牛云参数配置。

微信关注我哦 👍

大象工具微信公众号

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式