我无法使用带有用于Google Cload CDN的URLPrefix的签名URL。
我已经设置了一个存储桶,这是我的Cloud CDN实例的后端存储桶。我已经成功设置了URL签名密钥,并为特定路径生成了有效的签名URL,所有这些都使用https://cloud.google.com/cdn/docs/using-signed-urls?hl=en_US
中的说明使用下面的signCdnUrl2函数,可以为特定资源生成有效的签名url,例如
https://example.com/foo.mp4?Expires=[EXPIRATION]&KeyName=[KEY_NAME]&Signature=[SIGNATURE]
export function signCdnUrl2(fileName: string, opts: SignedUrlOptions, urlPrefix?: string) {
const expireVal = '' + new Date().getTime() + opts.expires;
const urlToSign = `${opts.baseUrl}/${fileName}?Expires=${expireVal}&KeyName=${opts.keyName}`;
// Compute signature
const keyBuffer = Buffer.from(opts.keyBase64, 'base64');
let signature = createHmac('sha1', keyBuffer).update(urlToSign).digest('base64');
signature = Base64urlUtil.escape(signature);
// Add signature to urlToSign and return signedUrl
return urlToSign + `&Signature=${signature}`;
}
我想避免“需要为每个不同的URL创建新的签名”,因此我正在按照https://cloud.google.com/cdn/docs/using-signed-urls?hl=en_US#url-prefix上的说明添加“ URL Prefix”选项。
我无法成功产生带有前缀的有效签名网址。我的当前尝试次数低于
export function signCdnUrl3(fileName: string, opts: SignedUrlOptions, urlPrefix?: string) {
const expireVal = '' + new Date().getTime() + opts.expires;
const urlPrefixCombined = `${opts.baseUrl}${urlPrefix}`;
// UrlPrefix param if provided otherwise empty string
const urlPrefixEncoded = urlPrefix ? Base64urlUtil.encode(urlPrefixCombined) : '';
// Param string to be signed with key
const paramsToSign = `URLPrefix=${urlPrefixEncoded}&Expires=${expireVal}&KeyName=${opts.keyName}`;
// Compute signature
const keyBuffer = Buffer.from(opts.keyBase64, 'base64');
let signature = createHmac('sha1', keyBuffer).update(paramsToSign).digest('base64');
signature = Base64urlUtil.escape(signature);
// Add signature to url
return `${opts.baseUrl}/${fileName}?${paramsToSign}&Signature=${signature}`;
}
如果我尝试访问存储桶的根目录下给定前缀下的任何资源,则会收到来自云CDN的403响应
负载均衡器的日志条目显示它正在将其检测为无效签名
说明中是否有我解释错误的内容,或者我只是在实现中错过了某些内容?任何指导将不胜感激。
添加了Base64Util代码以提高完整性
export class Base64urlUtil {
public static encode(str: string, encoding: any = 'utf8'): string {
const buffer: Buffer = Buffer.from(str, encoding);
const encodedStr: string = buffer.toString('base64');
const final: string = Base64urlUtil.escape(encodedStr);
return final;
}
public static decode(str: string, encoding?: string): string {
return Buffer.from(Base64urlUtil.unescape(str), 'base64').toString(encoding || 'utf8');
}
public static escape(str: string): string {
return str.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
public static unescape(str: string): string {
return (str + '==='.slice((str.length + 3) % 4))
.replace(/-/g, '+')
.replace(/_/g, '/');
}
}
更新
使用@elithrar https://stackoverflow.com/a/61315372/4330441提供的实现,我将他在signedParams中的样本值换成了自己的实时值。
let signedParams = signURLPrefix(
"https://<my-server>/sample/360p/",
1588291200,
"<my-key>",
"<valid-key>"
)
结果是这样
URLPrefix = aHR0cHM6Ly9zcHluYWwucmNmc29mdHdhcmUuaW8vc2FtcGxlLzM2MHAv&Expires = 1588291200&KeyName = my-key-name&Signature = wrbOloT + m31ZnQZei2Csqq0XaGY =
当我随后添加以下查询参数以在此地址调用cloud cdn端点时:
我在CDN日志中得到相同的403响应和匹配的无效签名
尝试使用两个不同的签名密钥,它们可以很好地用于对不带url前缀的单个特定url进行签名。
答案 0 :(得分:1)
目前尚不清楚什么地方出了问题-我怀疑您可能是对签名进行了双base64编码,但是您提供的代码段中未包含Base64urlUtil
。
这是一个工作版本,它与Go sample code的测试生成相同的签名:
const crypto = require("crypto")
export function signURLPrefix(
urlPrefix: string,
expires: number,
keyName: string,
key: string
) {
const expireVal = expires
const urlPrefixEncoded = Buffer.from(urlPrefix)
.toString("base64")
.replace(/_/g, '/')
.replace(/-/g, '+')
// Param string to be signed with key
const paramsToSign = `URLPrefix=${urlPrefixEncoded}&Expires=${expireVal}&KeyName=${keyName}`
// Compute signature
const keyBytes = Buffer.from(key, "base64")
// Expected key: []byte{0x9d, 0x9b, 0x51, 0xa2, 0x17, 0x4d, 0x17, 0xd9,
// 0xb7, 0x70, 0xa3, 0x36, 0xe0, 0x87, 0x0a, 0xe3}
let signature = crypto
.createHmac("sha1", keyBytes)
.update(paramsToSign)
.digest("base64")
.replace(/_/g, '/')
.replace(/-/g, '+')
return `${paramsToSign}&Signature=${signature}`
}
let signedParams = signURLPrefix(
"https://media.example.com/segments/",
1558131350,
"my-key",
"nZtRohdNF9m3cKM24IcK4w=="
)
let expected =
"URLPrefix=aHR0cHM6Ly9tZWRpYS5leGFtcGxlLmNvbS9zZWdtZW50cy8=&Expires=1558131350&KeyName=my-key&Signature=HWE5tBTZgnYVoZzVLG7BtRnOsgk="
if (signedParams === expected) {
console.log("✔️ Signature matches")
} else {
console.error(
`❌ Does not match: \n\tgot ${signedParams},\n\twant ${expected}`
)
}
输出:
➜ ts-node signed_prefix.ts
✔️ Signature matches
答案 1 :(得分:1)
我对此有同样的问题。带有URLPrefix的签名url和签名的cookie均不起作用。我曾尝试使用Golang / Ruby进行实现,并且我确信签名的逻辑与Golang示例相同。
在询问Google支持人员后,他们说:“由于该功能最近已移至Google Analytics(分析),因此他们发现您的项目未正确启用此功能。我们正在实施一项修复程序来解决此问题,我希望此功能将完全解决将于下周推出。一旦应用了此修复程序,我将向您更新。”我认为您的项目中也未正确启用它。
下周应用此修复程序后,我将重试并更新信息。
更新
我们收到了来自Google支持的最新回复,即我们的项目已启用该功能。我的代码无需任何修改即可工作。