Эх сурвалжийг харах

refactor(waf): 重构 SSL 证书处理逻辑

- 新增 SSL 结构体用于统一处理 SSL 证书相关数据
- 添加 AddSSLPolicy 和 EditSSL 方法封装 SSL 证书的完整生命周期管理- 优化 WebForwarding 服务中的 SSL 证书处理逻辑,使用新的 SSL 结构体和方法- 代码结构更加清晰,便于维护和扩展
huangjl 1 сар өмнө
parent
commit
295918cee3

+ 21 - 11
api/v1/wafformatter.go

@@ -1,22 +1,32 @@
 package v1
 
 type GlobalRequire struct {
-	HostId            int    `form:"hostId" json:"hostId" binding:"required"`
-	Uid               int    `form:"uid" json:"uid" binding:"required"`
-	Comment           string `form:"comment" json:"comment" binding:"required"`
-	Domain            string `form:"domain" json:"domain"`
+	HostId  int    `form:"hostId" json:"hostId" binding:"required"`
+	Uid     int    `form:"uid" json:"uid" binding:"required"`
+	Comment string `form:"comment" json:"comment" binding:"required"`
+	Domain  string `form:"domain" json:"domain"`
 }
 
 type GetForwardingRequest struct {
-	HostId            int    `form:"hostId" json:"hostId" binding:"required"`
-	Uid               int    `form:"uid" json:"uid" binding:"required"`
-	Id                int    `form:"id" json:"id" binding:"required"`
+	HostId int `form:"hostId" json:"hostId" binding:"required"`
+	Uid    int `form:"uid" json:"uid" binding:"required"`
+	Id     int `form:"id" json:"id" binding:"required"`
 }
 
-
 type WebJson struct {
 	BackendList string `json:"backendList"`
-	Host        string   `json:"host"`
-	ApiType  	string 	  `json:"apiType"`
-	Comment     string   `json:"comment"`
+	Host        string `json:"host"`
+	ApiType     string `json:"apiType"`
+	Comment     string `json:"comment"`
+}
+
+type SSL struct {
+	WebId       int64  `form:"WebId" json:"WebId" `
+	SSLPolicyId int    `form:"SSLPolicyId" json:"SSLPolicyId" `
+	CdnUserId   int    `form:"UserId" json:"UserId" `
+	Domain      string `form:"Domain" json:"Domain" `
+	Name        string `form:"Name" json:"Name" `
+	Description string `form:"Description" json:"Description" `
+	CertData    string `form:"CertData" json:"CertData" `
+	KeyData     string `form:"KeyData" json:"KeyData" `
 }

+ 105 - 0
internal/service/wafformatter.go

@@ -41,6 +41,8 @@ type WafFormatterService interface {
 	ConvertToPunycodeIfIDN(ctx context.Context, domain string) (isIDN bool, punycodeDomain string, err error)
 	// 解析证书
 	ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error)
+	AddSSLPolicy(ctx context.Context, req v1.SSL) (sslPolicyId int64, sslCertId int64, err error)
+	EditSSL(ctx context.Context, req v1.SSL) error
 }
 
 func NewWafFormatterService(
@@ -533,3 +535,106 @@ func (s *wafFormatterService) ParseCert(ctx context.Context, httpsCert string, h
 	return serverName, commonNames, DNSNames, x509Cert.NotBefore.Unix(), x509Cert.NotAfter.Unix(), isSelfSigned, nil
 
 }
+
+// HandleSSLPolicy 负责处理SSL证书的完整生命周期:解析、上传到CDN并创建或更新SSL策略。
+// 它封装了与CDN服务交互的复杂性,并返回一个可用的SSL策略ID。
+func (s *wafFormatterService) AddSSLPolicy(ctx context.Context, req v1.SSL) (sslPolicyId int64, sslCertId int64, err error) {
+	// 1. 解析证书文件,提取元数据
+	serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
+	if err != nil {
+		return 0, 0, fmt.Errorf("解析证书失败: %w", err)
+	}
+
+	// 2. 将证书添加到CDN提供商
+	// 这是获取可以在策略中引用的 `sslCertId` 的前提
+	newSslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
+		IsOn:         true,
+		UserId:       int64(req.CdnUserId),
+		Name:         req.Domain, // 使用域名作为证书名称
+		ServerName:   serverName,
+		Description:  req.Description,
+		CertData:     []byte(req.CertData),
+		KeyData:      []byte(req.KeyData),
+		TimeBeginAt:  before,
+		TimeEndAt:    after,
+		DnsNames:     DNSNames,
+		CommonNames:  commonNames,
+		IsSelfSigned: isSelfSigned,
+	})
+	if err != nil {
+		return 0, 0, fmt.Errorf("添加SSL证书到CDN失败: %w", err)
+	}
+
+	// 3. 基于获取到的证书ID,创建SSL策略
+	if newSslCertId != 0 {
+		// 构造策略中引用的证书列表
+		type sslCerts struct {
+			IsOn   bool  `json:"isOn" form:"isOn"`
+			CertId int64 `json:"certId" form:"certId"`
+		}
+		var sslCertsSlice []sslCerts
+		sslCertsSlice = append(sslCertsSlice, sslCerts{
+			IsOn:   true,
+			CertId: newSslCertId,
+		})
+		sslCertsJson, err := json.Marshal(sslCertsSlice)
+		if err != nil {
+			return 0, 0, fmt.Errorf("序列化SSL证书引用失败: %w", err)
+		}
+
+		// 调用CDN服务创建策略
+		newSslPolicyId, err := s.cdn.AddSSLPolicy(ctx, v1.AddSSLPolicy{
+			Http2Enabled: true,
+			SslCertsJSON: sslCertsJson,
+			MinVersion:   "TLS 1.1", // 可根据安全要求调整
+		})
+		if err != nil {
+			// 如果策略创建失败,需要考虑回滚或记录错误,这里直接返回错误
+			return 0, 0, fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
+		}
+		return newSslPolicyId, newSslCertId, nil
+	}
+
+	return 0, 0, fmt.Errorf("未能创建有效的SSL证书ID,无法继续创建策略")
+}
+
+func (s *wafFormatterService) EditSSL(ctx context.Context, req v1.SSL) error {
+	oldData, err := s.webForwardingRep.GetWebForwarding(ctx, req.WebId)
+	if err != nil {
+		return err
+	}
+	if oldData.HttpsKey != req.KeyData || oldData.HttpsCert != req.CertData {
+		serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
+		if err != nil {
+			return fmt.Errorf("解析证书失败: %w", err)
+		}
+		sslCert, err := s.webForwardingRep.GetSslCertId(ctx, oldData.SslCertId)
+		if err != nil {
+			return fmt.Errorf("获取SSL证书失败: %w", err)
+		}
+
+		for _, v := range sslCert {
+			err = s.cdn.EditSSLCert(ctx, v1.SSlCert{
+				SslCertId:    v.CertId,
+				IsOn:         v.IsOn,
+				UserId:       int64(req.CdnUserId),
+				Name:         req.Domain, // 使用域名作为证书名称
+				ServerName:   serverName,
+				Description:  req.Description,
+				CertData:     []byte(req.CertData),
+				KeyData:      []byte(req.KeyData),
+				TimeBeginAt:  before,
+				TimeEndAt:    after,
+				DnsNames:     DNSNames,
+				CommonNames:  commonNames,
+				IsSelfSigned: isSelfSigned,
+			})
+			if err != nil {
+				return fmt.Errorf("更新SSL证书失败: %w", err)
+			}
+		}
+		return nil
+
+	}
+	return nil
+}

+ 24 - 47
internal/service/webforwarding.go

@@ -251,56 +251,33 @@ func (s *webForwardingService) buildProxyJSONConfig(ctx context.Context, req *v1
 	// 判断协议类型,并处理 HTTPS 的特殊逻辑(证书)
 	if req.WebForwardingData.IsHttps == isHttps {
 		// 处理证书信息
-		serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.wafformatter.ParseCert(ctx, req.WebForwardingData.HttpsCert, req.WebForwardingData.HttpsKey)
-		if err != nil {
-			return nil, 0, fmt.Errorf("解析证书失败: %w", err)
-		}
-
-		// 添加 SSL 证书
-
-		sslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
-			IsOn:         true,
-			UserId:       int64(require.CdnUid),
-			Name:         req.WebForwardingData.Domain,
-			ServerName:   serverName,
-			Description:  req.WebForwardingData.Comment,
-			CertData:     []byte(req.WebForwardingData.HttpsCert),
-			KeyData:      []byte(req.WebForwardingData.HttpsKey),
-			TimeBeginAt:  before,
-			TimeEndAt:    after,
-			DnsNames:     DNSNames,
-			CommonNames:  commonNames,
-			IsSelfSigned: isSelfSigned,
-		})
-		if err != nil {
-			return nil, 0, fmt.Errorf("添加SSL证书失败: %w", err)
-		}
-
-		// 添加 SSL 策略
-		if sslCertId != 0 {
-			type sslCerts struct {
-				IsOn   bool  `json:"isOn" form:"isOn"`
-				CertId int64 `json:"certId" form:"certId"`
-			}
-			var sslCertsSlice []sslCerts
-			sslCertsSlice = append(sslCertsSlice, sslCerts{
-				IsOn:   true,
-				CertId: sslCertId,
+		if req.WebForwardingData.SslCertId == 0 {
+			sslPolicyId, _, err = s.wafformatter.AddSSLPolicy(ctx, v1.SSL{
+				CdnUserId:   require.CdnUid,
+				Domain:      req.WebForwardingData.Domain,
+				Name:        req.WebForwardingData.Domain,
+				Description: req.WebForwardingData.Comment,
+				CertData:    req.WebForwardingData.HttpsCert,
+				KeyData:     req.WebForwardingData.HttpsKey,
 			})
-			sslCertsJson, err := json.Marshal(sslCertsSlice)
 			if err != nil {
-				return nil, 0, fmt.Errorf("序列化SSL证书失败: %w", err)
+				return nil, 0, fmt.Errorf("处理证书失败: %w", err)
 			}
-
-			sslPolicyId, err = s.cdn.AddSSLPolicy(ctx, v1.AddSSLPolicy{
-				Http2Enabled: true,
-				SslCertsJSON: sslCertsJson,
-				MinVersion:   "TLS 1.1",
+			jsonData.SslPolicyRef.SslPolicyId = sslPolicyId
+		} else {
+			err = s.wafformatter.EditSSL(ctx, v1.SSL{
+				WebId:       int64(req.WebForwardingData.Id),
+				SSLPolicyId: int(req.WebForwardingData.SslCertId),
+				CdnUserId:   require.CdnUid,
+				Name:        req.WebForwardingData.Domain,
+				Description: req.WebForwardingData.Comment,
+				CertData:    req.WebForwardingData.HttpsCert,
+				KeyData:     req.WebForwardingData.HttpsKey,
 			})
 			if err != nil {
-				return nil, 0, fmt.Errorf("添加SSL策略失败: %w", err)
+				return nil, 0, fmt.Errorf("处理证书失败: %w", err)
 			}
-			jsonData.SslPolicyRef.SslPolicyId = sslPolicyId
+			jsonData.SslPolicyRef.SslPolicyId = req.WebForwardingData.SslCertId
 		}
 		jsonData.SslPolicyRef.IsOn = true
 	} else {
@@ -461,12 +438,12 @@ func (s *webForwardingService) AddWebForwarding(ctx context.Context, req *v1.Web
 
 func (s *webForwardingService) EditWebForwarding(ctx context.Context, req *v1.WebForwardingRequest) error {
 
-	require, formData, err := s.prepareWafData(ctx, req)
+	oldData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(req.WebForwardingData.Id))
 	if err != nil {
 		return err
 	}
-
-	oldData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(req.WebForwardingData.Id))
+	req.WebForwardingData.SslCertId = int64(oldData.SslCertId)
+	require, formData, err := s.prepareWafData(ctx, req)
 	if err != nil {
 		return err
 	}