Google Cloud CDN signedurl 使用 URLPrefix
Google Cloud CDN signedurl using URLPrefix
我无法使用 URL 前缀为 Google Cload CDN 签名 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}`;
}
我想避免 "the need to create a new signature for each distinct URL",所以我按照 https://cloud.google.com/cdn/docs/using-signed-urls?hl=en_US#url-prefix 中的说明添加 URL 前缀选项。
我无法成功生成带前缀的工作签名 url。我目前的尝试低于
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, '/');
}
}
Update
使用@elithrar 提供的实现,我将 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=
当我附加这些查询参数以调用此地址的云 cdn 端点时:
我在 cdn 日志中得到相同的 403 响应和匹配的无效签名
尝试使用两个不同的签名密钥,它们在没有 url 前缀的情况下对单个特定的 url 签名效果很好。
尚不完全清楚哪里出了问题 - 我怀疑您可能对签名进行了双 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
我也有同样的问题。使用 URLPrefix 签名 url 和签名 cookie 都不起作用。我曾尝试用 Golang/Ruby 实现,我确信签名逻辑与 Golang 示例相同。
询问 Google 支持后,他们说:"As this feature was recently moved to GA, they found that it wasn't correctly enabled on your project. A fix is being implemented to address this issue and I expect it to be completely rolled out by next week. I will update you once the fix has been applied." 我认为它也没有在您的项目中正确启用。
我将在下周应用修复后重试并更新信息。
更新
我们收到来自 Google 支持的最新回复,我们的项目已启用该功能。我的代码无需任何修改即可工作。
我无法使用 URL 前缀为 Google Cload CDN 签名 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}`;
}
我想避免 "the need to create a new signature for each distinct URL",所以我按照 https://cloud.google.com/cdn/docs/using-signed-urls?hl=en_US#url-prefix 中的说明添加 URL 前缀选项。
我无法成功生成带前缀的工作签名 url。我目前的尝试低于
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, '/');
}
}
Update
使用@elithrar
let signedParams = signURLPrefix(
"https://<my-server>/sample/360p/",
1588291200,
"<my-key>",
"<valid-key>"
)
结果是这样的:
URLPrefix=aHR0cHM6Ly9zcHluYWwucmNmc29mdHdhcmUuaW8vc2FtcGxlLzM2MHAv&Expires=1588291200&KeyName=my-key-name&Signature=wrbOloT+m31ZnQZei2Csqq0XaGY=
当我附加这些查询参数以调用此地址的云 cdn 端点时:
我在 cdn 日志中得到相同的 403 响应和匹配的无效签名
尝试使用两个不同的签名密钥,它们在没有 url 前缀的情况下对单个特定的 url 签名效果很好。
尚不完全清楚哪里出了问题 - 我怀疑您可能对签名进行了双 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
我也有同样的问题。使用 URLPrefix 签名 url 和签名 cookie 都不起作用。我曾尝试用 Golang/Ruby 实现,我确信签名逻辑与 Golang 示例相同。
询问 Google 支持后,他们说:"As this feature was recently moved to GA, they found that it wasn't correctly enabled on your project. A fix is being implemented to address this issue and I expect it to be completely rolled out by next week. I will update you once the fix has been applied." 我认为它也没有在您的项目中正确启用。
我将在下周应用修复后重试并更新信息。
更新
我们收到来自 Google 支持的最新回复,我们的项目已启用该功能。我的代码无需任何修改即可工作。