package oss import ( "context" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "hash" "io" "time" "dd_fiber_api/config" "dd_fiber_api/pkg/database" "github.com/aliyun/credentials-go/credentials" ) // PolicyToken 结构体用于存储生成的表单数据 type PolicyToken struct { Policy string `json:"policy"` SecurityToken string `json:"security_token"` SignatureVersion string `json:"x_oss_signature_version"` Credential string `json:"x_oss_credential"` Date string `json:"x_oss_date"` Signature string `json:"signature"` Host string `json:"host"` Dir string `json:"dir"` } // Service OSS服务结构体 type Service struct { Region string BucketName string AccessKeyID string AccessKeySecret string RoleARN string RoleSessionName string Redis *database.RedisClient } // NewService 创建OSS服务实例 func NewService(cfg *config.OSSConfig, redisClient *database.RedisClient) *Service { return &Service{ Region: cfg.Region, BucketName: cfg.BucketName, AccessKeyID: cfg.AccessKeyID, AccessKeySecret: cfg.AccessKeySecret, RoleARN: cfg.RoleARN, RoleSessionName: cfg.RoleSessionName, Redis: redisClient, } } // GetPolicyToken 生成OSS上传所需的签名和凭证 func (s *Service) GetPolicyToken(dir string) (*PolicyToken, error) { ctx := context.Background() // 生成缓存键(添加服务前缀避免重复,使用日期避免旧缓存) today := time.Now().Format("2006-01-02") cacheKey := fmt.Sprintf("ali_sts_credentials:%s:%s", today, dir) // 尝试从Redis获取缓存的凭证 if s.Redis != nil { var cachedToken PolicyToken if err := s.Redis.Get(ctx, cacheKey, &cachedToken); err == nil { return &cachedToken, nil } } // 从配置获取参数 accessKeyId := s.AccessKeyID accessKeySecret := s.AccessKeySecret roleArn := s.RoleARN roleSessionName := s.RoleSessionName if accessKeyId == "" || accessKeySecret == "" || roleArn == "" { return nil, fmt.Errorf("缺少必要的配置参数: AccessKeyID, AccessKeySecret, RoleARN") } // 设置OSS上传地址 host := fmt.Sprintf("https://%s.oss-%s.aliyuncs.com", s.BucketName, s.Region) config := new(credentials.Config). SetType("ram_role_arn"). SetAccessKeyId(accessKeyId). SetAccessKeySecret(accessKeySecret). SetRoleArn(roleArn). SetRoleSessionName(roleSessionName). SetPolicy(""). SetRoleSessionExpiration(3600) // 根据配置创建凭证提供器 provider, err := credentials.NewCredential(config) if err != nil { return nil, fmt.Errorf("创建凭证提供器失败: %v", err) } // 从凭证提供器获取凭证 accessKeyIdResult, err := provider.GetAccessKeyId() if err != nil { return nil, fmt.Errorf("获取AccessKeyId失败: %v", err) } accessKeySecretResult, err := provider.GetAccessKeySecret() if err != nil { return nil, fmt.Errorf("获取AccessKeySecret失败: %v", err) } securityToken, err := provider.GetSecurityToken() if err != nil { return nil, fmt.Errorf("获取SecurityToken失败: %v", err) } // 构建policy utcTime := time.Now().UTC() date := utcTime.Format("20060102") expiration := utcTime.Add(1 * time.Hour) policyMap := map[string]any{ "expiration": expiration.Format("2006-01-02T15:04:05.000Z"), "conditions": []any{ map[string]string{"bucket": s.BucketName}, map[string]string{"x-oss-signature-version": "OSS4-HMAC-SHA256"}, map[string]string{"x-oss-credential": fmt.Sprintf("%v/%v/%v/oss/aliyun_v4_request", *accessKeyIdResult, date, s.Region)}, map[string]string{"x-oss-date": utcTime.Format("20060102T150405Z")}, map[string]string{"x-oss-security-token": *securityToken}, }, } // 将policy转换为JSON格式 policy, err := json.Marshal(policyMap) if err != nil { return nil, fmt.Errorf("序列化policy失败: %v", err) } // 构造待签名字符串(StringToSign) stringToSign := base64.StdEncoding.EncodeToString([]byte(policy)) hmacHash := func() hash.Hash { return sha256.New() } // 构建signing key signingKey := "aliyun_v4" + *accessKeySecretResult h1 := hmac.New(hmacHash, []byte(signingKey)) io.WriteString(h1, date) h1Key := h1.Sum(nil) h2 := hmac.New(hmacHash, h1Key) io.WriteString(h2, s.Region) h2Key := h2.Sum(nil) h3 := hmac.New(hmacHash, h2Key) io.WriteString(h3, "oss") h3Key := h3.Sum(nil) h4 := hmac.New(hmacHash, h3Key) io.WriteString(h4, "aliyun_v4_request") h4Key := h4.Sum(nil) // 生成签名 h := hmac.New(hmacHash, h4Key) io.WriteString(h, stringToSign) signature := hex.EncodeToString(h.Sum(nil)) // 构建返回给前端的表单 policyToken := &PolicyToken{ Policy: stringToSign, SecurityToken: *securityToken, SignatureVersion: "OSS4-HMAC-SHA256", Credential: fmt.Sprintf("%v/%v/%v/oss/aliyun_v4_request", *accessKeyIdResult, date, s.Region), Date: utcTime.UTC().Format("20060102T150405Z"), Signature: signature, Host: host, Dir: dir, } // 将凭证缓存到Redis(1小时过期) if s.Redis != nil { _ = s.Redis.Set(ctx, cacheKey, policyToken, time.Hour) } return policyToken, nil } // GetMockPolicyToken 生成模拟凭证(用于测试) func (s *Service) GetMockPolicyToken(dir string) *PolicyToken { host := fmt.Sprintf("https://%s.oss-%s.aliyuncs.com", s.BucketName, s.Region) return &PolicyToken{ Policy: "mock_policy", SecurityToken: "mock_security_token", SignatureVersion: "OSS4-HMAC-SHA256", Credential: "mock_credential", Date: "20241022T150000Z", Signature: "mock_signature", Host: host, Dir: dir, } }