package fileauth import ( "encoding/base64" "encoding/json" "errors" "strings" ) // UploadTokenParts 上传凭证分步结果(FileAuth.java:AccessKey:encodedSign:policyB64)。 type UploadTokenParts struct { AccessKey string PolicyJSON string EncodedPutPolicy string // 第三段:policy JSON 的 URL-safe Base64 SignInput string // HMAC 输入:policy JSON 明文 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"` } // BuildUploadTokenParts 分步生成上传凭证(生产 upload profile,scope=仅 bucket)。 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, Detail: "uploadToken = AccessKey + ':' + encodedSign + ':' + policyB64(按前两处冒号拆分)", }, { 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 != "", Detail: "第 3 段 policyB64(URL-safe Base64 policy JSON)非空", }, } 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 } // DecodePutPolicyB64 解码 policyB64 为 JSON 字符串(美化缩进)。 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 }