Files
xk-hy-transit-go/internal/fileauth/parts.go

129 lines
3.5 KiB
Go
Raw Normal View History

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.javaAccessKey: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 profilescope=仅 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:policyB64expectedAccessKey 非空时校验首段。
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 段 encodedSignURL-safe Base64 HMAC非空",
},
{
ID: "encoded_policy_non_empty",
OK: ok && pol != "",
2026-05-28 17:03:08 +08:00
Detail: "第 3 段 policyB64URL-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
}