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:
joywayer
2026-06-24 02:09:28 +08:00
parent 7d89a9d3ee
commit c3a0ddbf8b
@@ -22,25 +22,24 @@ public enum QiniuTokenSigner {
/// token
/// - Parameters:
/// - key: keyscope = bucketallow overridescope = bucket:key
/// - key: putPolicydaoqi
/// 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 JSONdaoqi 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: "")
}
}