3.B 修复:七牛 token 401 bad token(base64URL padding + scope 字段)
## 问题 录音 + 转码正常,但七牛上传报: status: 401 / error: bad token / QiniuUploadError error 1 ## 根因 逐字段对照 daoqi QiniuManager.m:202-236 generateUploadToken,发现两处 关键不一致: ### 1. base64URL padding:新外壳删了 `=`,daoqi 保留 daoqi urlsafeBase64EncodeData:240-245 只替换 `+→-` 和 `/→_`,**保留 末尾 `=` padding**。 新外壳额外做了 `=→空` 替换,本意符合 RFC 4648 §5 base64URL 的可选 padding 删除,但七牛后端 decode token 时**依赖 padding 还原 base64**, 删 `=` 会让 HMAC 验签数据流不一致 → 401 bad token。 ### 2. scope 字段:新外壳带了 `:key` 后缀,daoqi 只用 bucket daoqi QiniuManager.m:207 putPolicy.scope = bucketName(限定 bucket, 允许任意 key)。 新外壳之前传 key 参数后构造 `bucket:key`(限定 key 精确匹配),与 daoqi 行为不一致。带 `:key` 后缀也是七牛合法 scope,但七牛后端 校验更严,且无必要——key 已经在 putFile API 单独传递,token 内不必 重复声明。 ## 修复 - base64URLEncode:移除 `=→空` 替换 - uploadToken(key:):key 参数标记为废弃但保留(避免破坏调用方), putPolicy.scope 改为只用 bucket - 顺带:JSONSerialization 不再强制 sortedKeys(daoqi 用 options:0) ## 验证 BuildProject 通过 0 错误。真机重试录音上传应不再 401。
This commit is contained in:
@@ -22,25 +22,24 @@ public enum QiniuTokenSigner {
|
||||
|
||||
/// 生成上传 token。
|
||||
/// - Parameters:
|
||||
/// - key: 上传文件 key(可空:scope = bucket,allow override;非空:scope = bucket:key)
|
||||
/// - key: 已废弃参数;保留签名兼容性,但不再写入 putPolicy。daoqi
|
||||
/// QiniuManager.m:207 的 putPolicy.scope 仅含 bucket(限定 bucket、
|
||||
/// 允许任意 key),与新外壳上传 key 是分离的两个维度。带 `:key` 后
|
||||
/// 缀(限定 key 精确匹配)会让七牛后端校验更严,且与 daoqi 行为不一致
|
||||
/// - expiresIn: token 有效时长(默认 3600 秒)
|
||||
public nonisolated static func uploadToken(key: String? = nil, expiresIn: TimeInterval = 3600) -> String {
|
||||
// 1. 拼 scope
|
||||
let scope: String
|
||||
if let key, !key.isEmpty {
|
||||
scope = "\(QiniuConfig.bucketName):\(key)"
|
||||
} else {
|
||||
scope = QiniuConfig.bucketName
|
||||
}
|
||||
_ = key // 参数保留以避免破坏调用方,但不再使用
|
||||
|
||||
// 1. scope 仅含 bucket(对齐 daoqi QiniuManager.m:207)
|
||||
let scope = QiniuConfig.bucketName
|
||||
let deadline = Int(Date().timeIntervalSince1970 + expiresIn)
|
||||
|
||||
// 2. putPolicy JSON(注意 key 顺序与 msext 等价:scope / deadline,
|
||||
// 实际七牛后端不要求顺序,但保持与 msext 一致便于排错)
|
||||
// 2. putPolicy JSON(daoqi 用默认 options:0,不强制排序)
|
||||
let putPolicy: [String: Any] = [
|
||||
"scope": scope,
|
||||
"deadline": deadline
|
||||
]
|
||||
let policyData = (try? JSONSerialization.data(withJSONObject: putPolicy, options: [.sortedKeys])) ?? Data()
|
||||
let policyData = (try? JSONSerialization.data(withJSONObject: putPolicy, options: [])) ?? Data()
|
||||
|
||||
// 3. Base64URL(putPolicy)
|
||||
let encodedPolicy = base64URLEncode(policyData)
|
||||
@@ -58,12 +57,12 @@ public enum QiniuTokenSigner {
|
||||
return "\(QiniuConfig.accessKey):\(encodedSign):\(encodedPolicy)"
|
||||
}
|
||||
|
||||
/// Base64URL: 标准 Base64 把 + → -、/ → _、去掉末尾 =
|
||||
/// 与七牛 SDK `QNUrlSafeBase64.encodeData:` 等价
|
||||
/// Base64URL: 标准 Base64 把 + → -、/ → _,**保留末尾 `=` padding**。
|
||||
/// 对齐 daoqi QiniuManager.m:240-245 `urlsafeBase64EncodeData:`,七牛后端
|
||||
/// decode token 时依赖 padding 还原 base64;删 `=` 会触发 401 bad token。
|
||||
private nonisolated static func base64URLEncode(_ data: Data) -> String {
|
||||
data.base64EncodedString()
|
||||
.replacingOccurrences(of: "+", with: "-")
|
||||
.replacingOccurrences(of: "/", with: "_")
|
||||
.replacingOccurrences(of: "=", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user