2026-05-28 16:39:51 +08:00
|
|
|
|
package fileauth
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-28 17:03:08 +08:00
|
|
|
|
// UploadTokenParts 上传凭证分步结果(FileAuth.java:AccessKey:encodedSign:policyB64)。
|
2026-05-28 16:39:51 +08:00
|
|
|
|
type UploadTokenParts struct {
|
|
|
|
|
|
AccessKey string
|
|
|
|
|
|
PolicyJSON string
|
2026-05-28 17:03:08 +08:00
|
|
|
|
EncodedPutPolicy string // 第三段:policy JSON 的 URL-safe Base64
|
|
|
|
|
|
SignInput string // HMAC 输入:policy JSON 明文
|
2026-05-28 16:39:51 +08:00
|
|
|
|
EncodedSign string
|
|
|
|
|
|
UploadToken string
|
|
|
|
|
|
DeadlineUnix int64
|
|
|
|
|
|
ExpiresSec int64
|
|
|
|
|
|
Algorithm string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FormatCheck 单条 uploadToken 格式校验结果。
|
|
|
|
|
|
type FormatCheck struct {
|
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
|
OK bool `json:"ok"`
|
|
|
|
|
|
Detail string `json:"detail"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-28 17:03:08 +08:00
|
|
|
|
// BuildUploadTokenParts 分步生成上传凭证(生产 upload profile,scope=仅 bucket)。
|
2026-05-28 16:39:51 +08:00
|
|
|
|
func BuildUploadTokenParts(accessKey, secret, bucket string, deadlineUnix int64) (UploadTokenParts, error) {
|
|
|
|
|
|
return BuildUploadTokenPartsForUpload(accessKey, secret, bucket, deadlineUnix)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SplitUploadToken 按前两处英文冒号拆分为 accessKey、encodedSign、encodedPutPolicy。
|
|
|
|
|
|
func SplitUploadToken(token string) (accessKey, encodedSign, encodedPutPolicy string, ok bool) {
|
|
|
|
|
|
token = strings.TrimSpace(token)
|
|
|
|
|
|
i := strings.Index(token, ":")
|
|
|
|
|
|
if i <= 0 {
|
|
|
|
|
|
return "", "", "", false
|
|
|
|
|
|
}
|
|
|
|
|
|
rest := token[i+1:]
|
|
|
|
|
|
j := strings.Index(rest, ":")
|
|
|
|
|
|
if j <= 0 {
|
|
|
|
|
|
return "", "", "", false
|
|
|
|
|
|
}
|
|
|
|
|
|
ak := token[:i]
|
|
|
|
|
|
sign := rest[:j]
|
|
|
|
|
|
pol := rest[j+1:]
|
|
|
|
|
|
if ak == "" || sign == "" || pol == "" {
|
|
|
|
|
|
return "", "", "", false
|
|
|
|
|
|
}
|
|
|
|
|
|
return ak, sign, pol, true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ValidateUploadTokenFormat 校验 token 是否符合 AccessKey:sign:policyB64;expectedAccessKey 非空时校验首段。
|
|
|
|
|
|
func ValidateUploadTokenFormat(token, expectedAccessKey string) (allOK bool, checks []FormatCheck) {
|
|
|
|
|
|
ak, sign, pol, ok := SplitUploadToken(token)
|
|
|
|
|
|
checks = []FormatCheck{
|
|
|
|
|
|
{
|
|
|
|
|
|
ID: "three_parts",
|
|
|
|
|
|
OK: ok,
|
2026-05-28 17:03:08 +08:00
|
|
|
|
Detail: "uploadToken = AccessKey + ':' + encodedSign + ':' + policyB64(按前两处冒号拆分)",
|
2026-05-28 16:39:51 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
ID: "access_key_non_empty",
|
|
|
|
|
|
OK: ok && ak != "",
|
|
|
|
|
|
Detail: "第 1 段 AccessKey 非空",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
ID: "encoded_sign_non_empty",
|
|
|
|
|
|
OK: ok && sign != "",
|
|
|
|
|
|
Detail: "第 2 段 encodedSign(URL-safe Base64 HMAC)非空",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
ID: "encoded_policy_non_empty",
|
|
|
|
|
|
OK: ok && pol != "",
|
2026-05-28 17:03:08 +08:00
|
|
|
|
Detail: "第 3 段 policyB64(URL-safe Base64 policy JSON)非空",
|
2026-05-28 16:39:51 +08:00
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
if expectedAccessKey != "" {
|
|
|
|
|
|
exp := strings.TrimSpace(expectedAccessKey)
|
|
|
|
|
|
match := ok && ak == exp
|
|
|
|
|
|
checks = append(checks, FormatCheck{
|
|
|
|
|
|
ID: "access_key_match",
|
|
|
|
|
|
OK: match,
|
|
|
|
|
|
Detail: "第 1 段与当前 AccessKey 一致",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
_, decErr := DecodePutPolicyB64(pol)
|
|
|
|
|
|
checks = append(checks, FormatCheck{
|
|
|
|
|
|
ID: "policy_b64_decodable",
|
|
|
|
|
|
OK: ok && decErr == nil,
|
|
|
|
|
|
Detail: "第 3 段可 URL-safe Base64 解码为 JSON",
|
|
|
|
|
|
})
|
|
|
|
|
|
allOK = true
|
|
|
|
|
|
for _, c := range checks {
|
|
|
|
|
|
if !c.OK {
|
|
|
|
|
|
allOK = false
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return allOK, checks
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-28 17:03:08 +08:00
|
|
|
|
// DecodePutPolicyB64 解码 policyB64 为 JSON 字符串(美化缩进)。
|
2026-05-28 16:39:51 +08:00
|
|
|
|
func DecodePutPolicyB64(encoded string) (string, error) {
|
|
|
|
|
|
encoded = strings.TrimSpace(encoded)
|
|
|
|
|
|
if encoded == "" {
|
|
|
|
|
|
return "", errors.New("empty encoded policy")
|
|
|
|
|
|
}
|
|
|
|
|
|
raw, err := base64.RawURLEncoding.DecodeString(encoded)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
raw, err = base64.URLEncoding.DecodeString(encoded)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
var pretty interface{}
|
|
|
|
|
|
if err := json.Unmarshal(raw, &pretty); err != nil {
|
|
|
|
|
|
return string(raw), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
out, err := json.MarshalIndent(pretty, "", " ")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return string(raw), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
return string(out), nil
|
|
|
|
|
|
}
|