Browse Source

feat(cdn): 增加证书解析功能并优化 HTTPS 证书处理

- 在 CDN API 中添加 ServerName 字段用于绑定证书的域名
- 实现证书解析功能,包括获取 Common Name、DNS Names、证书有效期和自签名检测
- 更新 Web 转发服务,使用证书解析结果填充 SSL 证书信息
- 优化 SSL 证书添加逻辑,包含更多证书详情
huangjl 1 month ago
parent
commit
516916f179
4 changed files with 128 additions and 77 deletions
  1. 2 1
      api/v1/cdn.go
  2. 1 0
      internal/service/cdn.go
  3. 112 74
      internal/service/wafformatter.go
  4. 13 2
      internal/service/webforwarding.go

+ 2 - 1
api/v1/cdn.go

@@ -112,6 +112,7 @@ type SSlCert struct {
 	UserId       int64    `json:"userId" form:"userId"`             //用户id
 	UserId       int64    `json:"userId" form:"userId"`             //用户id
 	Name         string   `json:"name" form:"name"`                 //证书名称
 	Name         string   `json:"name" form:"name"`                 //证书名称
 	Description  string   `json:"description" form:"description"`   //证书描述
 	Description  string   `json:"description" form:"description"`   //证书描述
+	ServerName   string   `json:"serverName" form:"serverName"`     //证书绑定的域名
 	IsCA         bool     `json:"isCA" form:"isCA"`                 //是否是CA证书
 	IsCA         bool     `json:"isCA" form:"isCA"`                 //是否是CA证书
 	CertData     []byte   `json:"certData" form:"certData"`         //证书内容
 	CertData     []byte   `json:"certData" form:"certData"`         //证书内容
 	KeyData      []byte   `json:"keyData" form:"keyData"`           //证书内容
 	KeyData      []byte   `json:"keyData" form:"keyData"`           //证书内容
@@ -133,7 +134,7 @@ type AddSSLPolicy struct {
 	MinVersion       string   `json:"minVersion" form:"minVersion"`             //最小TLS版本
 	MinVersion       string   `json:"minVersion" form:"minVersion"`             //最小TLS版本
 	SslCertsJSON     []byte   `json:"sslCertsJSON" form:"sslCertsJSON"`         //SslCertsJSON
 	SslCertsJSON     []byte   `json:"sslCertsJSON" form:"sslCertsJSON"`         //SslCertsJSON
 	HstsJSON         []byte   `json:"hstsJSON" form:"hstsJSON"`                 //HstsJSON
 	HstsJSON         []byte   `json:"hstsJSON" form:"hstsJSON"`                 //HstsJSON
-	ClientAuthType   int32   `json:"clientAuthType" form:"clientAuthType"`     //可选项,客户端校验类型:0 无需证书,1 需要客户端证书,2 需要任一客户端证书,3 如果客户端上传了证书才校验,4 需要客户端证书而且需要校验
+	ClientAuthType   int32    `json:"clientAuthType" form:"clientAuthType"`     //可选项,客户端校验类型:0 无需证书,1 需要客户端证书,2 需要任一客户端证书,3 如果客户端上传了证书才校验,4 需要客户端证书而且需要校验
 	CipherSuites     []string `json:"cipherSuites" form:"cipherSuites"`         //可选项,支持的TLS加密套件
 	CipherSuites     []string `json:"cipherSuites" form:"cipherSuites"`         //可选项,支持的TLS加密套件
 	CipherSuitesIsOn bool     `json:"cipherSuitesIsOn" form:"cipherSuitesIsOn"` //可选项,是否启用自定义加密套件
 	CipherSuitesIsOn bool     `json:"cipherSuitesIsOn" form:"cipherSuitesIsOn"` //可选项,是否启用自定义加密套件
 	OcspIsOn         bool     `json:"ocspIsOn" form:"ocspIsOn"`                 //可选项,是否开启OCSP
 	OcspIsOn         bool     `json:"ocspIsOn" form:"ocspIsOn"`                 //可选项,是否开启OCSP

+ 1 - 0
internal/service/cdn.go

@@ -580,6 +580,7 @@ func (s *cdnService) AddSSLCert(ctx context.Context, req v1.SSlCert) (int64, err
 		"isOn":         req.IsOn,
 		"isOn":         req.IsOn,
 		"userId":       req.UserId,
 		"userId":       req.UserId,
 		"name":         req.Name,
 		"name":         req.Name,
+		"serverName":   req.ServerName,
 		"description":  req.Description,
 		"description":  req.Description,
 		"isCA":         req.IsCA,
 		"isCA":         req.IsCA,
 		"certData":     req.CertData,
 		"certData":     req.CertData,

+ 112 - 74
internal/service/wafformatter.go

@@ -2,6 +2,8 @@ package service
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/tls"
+	"crypto/x509"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
 	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
@@ -23,23 +25,26 @@ type WafFormatterService interface {
 	Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error)
 	Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error)
 	validateWafPortCount(ctx context.Context, hostId int) error
 	validateWafPortCount(ctx context.Context, hostId int) error
 	validateWafDomainCount(ctx context.Context, req v1.GlobalRequire) error
 	validateWafDomainCount(ctx context.Context, req v1.GlobalRequire) error
-	ConvertToWildcardDomain(ctx context.Context,domain string) (string, error)
-	AppendWafIp(ctx context.Context, req []string,returnSourceIp string) ([]v1.IpInfo, error)
+	ConvertToWildcardDomain(ctx context.Context, domain string) (string, error)
+	AppendWafIp(ctx context.Context, req []string, returnSourceIp string) ([]v1.IpInfo, error)
 	WashIps(ctx context.Context, req []string) ([]string, error)
 	WashIps(ctx context.Context, req []string) ([]string, error)
-	PublishIpWhitelistTask(ips []string, action string,returnSourceIp string, color string)
+	PublishIpWhitelistTask(ips []string, action string, returnSourceIp string, color string)
 	PublishDomainWhitelistTask(domain, ip, action string)
 	PublishDomainWhitelistTask(domain, ip, action string)
 	findIpDifferences(oldIps, newIps []string) ([]string, []string)
 	findIpDifferences(oldIps, newIps []string) ([]string, []string)
 	WashDeleteWafIp(ctx context.Context, backendList []string) ([]string, error)
 	WashDeleteWafIp(ctx context.Context, backendList []string) ([]string, error)
-	WashEditWafIp(ctx context.Context, newBackendList []string,oldBackendList []string) ([]string, []string, error)
+	WashEditWafIp(ctx context.Context, newBackendList []string, oldBackendList []string) ([]string, []string, error)
 	//cdn添加网站
 	//cdn添加网站
 	AddOrigin(ctx context.Context, req v1.WebJson) (int64, error)
 	AddOrigin(ctx context.Context, req v1.WebJson) (int64, error)
 	// 获取ip数量等于1的源站过白ip
 	// 获取ip数量等于1的源站过白ip
 	WashDelIps(ctx context.Context, ips []string) ([]string, error)
 	WashDelIps(ctx context.Context, ips []string) ([]string, error)
 	// 判断域名是否是IDN,如果是,转换为 Punycode
 	// 判断域名是否是IDN,如果是,转换为 Punycode
-	ConvertToPunycodeIfIDN(ctx context.Context,domain string) (isIDN bool, punycodeDomain string, err error)
+	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)
 }
 }
+
 func NewWafFormatterService(
 func NewWafFormatterService(
-    service *Service,
+	service *Service,
 	globalRep repository.GlobalLimitRepository,
 	globalRep repository.GlobalLimitRepository,
 	hostRep repository.HostRepository,
 	hostRep repository.HostRepository,
 	required RequiredService,
 	required RequiredService,
@@ -54,45 +59,45 @@ func NewWafFormatterService(
 	cdn CdnService,
 	cdn CdnService,
 ) WafFormatterService {
 ) WafFormatterService {
 	return &wafFormatterService{
 	return &wafFormatterService{
-		Service:        service,
-		globalRep: globalRep,
-		hostRep: hostRep,
-		required: required,
-		parser: parser,
-		tcpforwardingRep: tcpforwardingRep,
-		udpForWardingRep: udpForWardingRep,
-		webForwardingRep: webForwardingRep,
-		host : host,
-		mq:    mq,
-		gatewayGroupRep: gatewayGroupRep,
+		Service:           service,
+		globalRep:         globalRep,
+		hostRep:           hostRep,
+		required:          required,
+		parser:            parser,
+		tcpforwardingRep:  tcpforwardingRep,
+		udpForWardingRep:  udpForWardingRep,
+		webForwardingRep:  webForwardingRep,
+		host:              host,
+		mq:                mq,
+		gatewayGroupRep:   gatewayGroupRep,
 		gatewayGroupIpRep: gatewayGroupIpRep,
 		gatewayGroupIpRep: gatewayGroupIpRep,
-		cdn: cdn,
+		cdn:               cdn,
 	}
 	}
 }
 }
 
 
 type wafFormatterService struct {
 type wafFormatterService struct {
 	*Service
 	*Service
-	globalRep repository.GlobalLimitRepository
-	hostRep repository.HostRepository
-	required RequiredService
-	parser ParserService
-	tcpforwardingRep repository.TcpforwardingRepository
-	udpForWardingRep repository.UdpForWardingRepository
-	webForwardingRep repository.WebForwardingRepository
-	host HostService
-	mq *rabbitmq.RabbitMQ
-	gatewayGroupRep repository.GatewayGroupRepository
+	globalRep         repository.GlobalLimitRepository
+	hostRep           repository.HostRepository
+	required          RequiredService
+	parser            ParserService
+	tcpforwardingRep  repository.TcpforwardingRepository
+	udpForWardingRep  repository.UdpForWardingRepository
+	webForwardingRep  repository.WebForwardingRepository
+	host              HostService
+	mq                *rabbitmq.RabbitMQ
+	gatewayGroupRep   repository.GatewayGroupRepository
 	gatewayGroupIpRep repository.GateWayGroupIpRepository
 	gatewayGroupIpRep repository.GateWayGroupIpRepository
-	cdn CdnService
+	cdn               CdnService
 }
 }
 
 
 type RequireResponse struct {
 type RequireResponse struct {
 	model.GlobalLimit `json:"globalLimit" form:"globalLimit"`
 	model.GlobalLimit `json:"globalLimit" form:"globalLimit"`
-	GatewayIps []string `json:"ips" form:"ips"`
-	Tag string `json:"tag" form:"tag"`
+	GatewayIps        []string `json:"ips" form:"ips"`
+	Tag               string   `json:"tag" form:"tag"`
 }
 }
 
 
-func (s *wafFormatterService) Require(ctx context.Context,req v1.GlobalRequire) (RequireResponse, error)  {
+func (s *wafFormatterService) Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error) {
 	var res RequireResponse
 	var res RequireResponse
 	// 获取全局配置信息
 	// 获取全局配置信息
 	globalLimit, err := s.globalRep.GetGlobalLimitByHostId(ctx, int64(req.HostId))
 	globalLimit, err := s.globalRep.GetGlobalLimitByHostId(ctx, int64(req.HostId))
@@ -115,7 +120,6 @@ func (s *wafFormatterService) Require(ctx context.Context,req v1.GlobalRequire)
 	return res, nil
 	return res, nil
 }
 }
 
 
-
 func (s *wafFormatterService) validateWafPortCount(ctx context.Context, hostId int) error {
 func (s *wafFormatterService) validateWafPortCount(ctx context.Context, hostId int) error {
 	congfig, err := s.host.GetGlobalLimitConfig(ctx, hostId)
 	congfig, err := s.host.GetGlobalLimitConfig(ctx, hostId)
 	if err != nil {
 	if err != nil {
@@ -133,7 +137,7 @@ func (s *wafFormatterService) validateWafPortCount(ctx context.Context, hostId i
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if int64(congfig.PortCount) > tcpCount + udpCount + webCount {
+	if int64(congfig.PortCount) > tcpCount+udpCount+webCount {
 		return nil
 		return nil
 	}
 	}
 	return fmt.Errorf("端口数量超出套餐限制,已配置%d个端口,套餐限制为%d个端口", tcpCount+udpCount+webCount, congfig.PortCount)
 	return fmt.Errorf("端口数量超出套餐限制,已配置%d个端口,套餐限制为%d个端口", tcpCount+udpCount+webCount, congfig.PortCount)
@@ -169,7 +173,7 @@ func (s *wafFormatterService) ConvertToWildcardDomain(ctx context.Context, domai
 	if err != nil {
 	if err != nil {
 		s.logger.Error("无效的域名", zap.String("domain", domain), zap.Error(err))
 		s.logger.Error("无效的域名", zap.String("domain", domain), zap.Error(err))
 		// 如果域名无效(如 IP 地址、localhost),则返回错误。
 		// 如果域名无效(如 IP 地址、localhost),则返回错误。
-		return "",nil
+		return "", nil
 	}
 	}
 
 
 	// 2. 比较原始域名和可注册域名。
 	// 2. 比较原始域名和可注册域名。
@@ -184,15 +188,15 @@ func (s *wafFormatterService) ConvertToWildcardDomain(ctx context.Context, domai
 	return domain, nil
 	return domain, nil
 }
 }
 
 
-func (s *wafFormatterService) AppendWafIp(ctx context.Context, req []string,returnSourceIp string) ([]v1.IpInfo, error) {
+func (s *wafFormatterService) AppendWafIp(ctx context.Context, req []string, returnSourceIp string) ([]v1.IpInfo, error) {
 	var ips []v1.IpInfo
 	var ips []v1.IpInfo
 	for _, v := range req {
 	for _, v := range req {
 		ips = append(ips, v1.IpInfo{
 		ips = append(ips, v1.IpInfo{
-			FType:      "0",
-			FStartIp:   v,
-			FEndIp:     v,
-			FRemark:    "宁波高防IP过白",
-			FServerIp:  returnSourceIp,
+			FType:     "0",
+			FStartIp:  v,
+			FEndIp:    v,
+			FRemark:   "宁波高防IP过白",
+			FServerIp: returnSourceIp,
 		})
 		})
 	}
 	}
 	return ips, nil
 	return ips, nil
@@ -206,11 +210,11 @@ func (s *wafFormatterService) AppendWafIpByRemovePort(ctx context.Context, req [
 			return nil, err
 			return nil, err
 		}
 		}
 		ips = append(ips, v1.IpInfo{
 		ips = append(ips, v1.IpInfo{
-			FType:      "0",
-			FStartIp:   ip,
-			FEndIp:     ip,
-			FRemark:    "宁波高防IP过白",
-			FServerIp:  "",
+			FType:     "0",
+			FStartIp:  ip,
+			FEndIp:    ip,
+			FRemark:   "宁波高防IP过白",
+			FServerIp: "",
 		})
 		})
 	}
 	}
 	return ips, nil
 	return ips, nil
@@ -220,7 +224,7 @@ func (s *wafFormatterService) AppendWafIpByRemovePort(ctx context.Context, req [
 func (s *wafFormatterService) WashIps(ctx context.Context, req []string) ([]string, error) {
 func (s *wafFormatterService) WashIps(ctx context.Context, req []string) ([]string, error) {
 	var res []string
 	var res []string
 	for _, v := range req {
 	for _, v := range req {
-		res = append(res,v)
+		res = append(res, v)
 	}
 	}
 	return res, nil
 	return res, nil
 }
 }
@@ -273,26 +277,25 @@ func (s *wafFormatterService) PublishDomainWhitelistTask(domain, ip, action stri
 	}
 	}
 }
 }
 
 
-
-func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string, returnSourceIp string,color string) {
+func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string, returnSourceIp string, color string) {
 	// Define message payload, including the action
 	// Define message payload, including the action
 	type ipTaskPayload struct {
 	type ipTaskPayload struct {
-		Ips     []string `json:"ips"`
-		Action string `json:"action"`
-		ReturnSourceIp string `json:"return_source_ip"`
-		Color string `json:"color"`
+		Ips            []string `json:"ips"`
+		Action         string   `json:"action"`
+		ReturnSourceIp string   `json:"return_source_ip"`
+		Color          string   `json:"color"`
 	}
 	}
 	payload := ipTaskPayload{
 	payload := ipTaskPayload{
-		Ips:     ips,
-		Action: action,
+		Ips:            ips,
+		Action:         action,
 		ReturnSourceIp: returnSourceIp,
 		ReturnSourceIp: returnSourceIp,
-		Color: color,
+		Color:          color,
 	}
 	}
 
 
 	// Serialize the message
 	// Serialize the message
 	msgBody, err := json.Marshal(payload)
 	msgBody, err := json.Marshal(payload)
 	if err != nil {
 	if err != nil {
-		s.logger.Error("序列化 IP 白名单任务消息失败", zap.Error(err), zap.Any("IPs", ips), zap.String("action", action),zap.String("color", color))
+		s.logger.Error("序列化 IP 白名单任务消息失败", zap.Error(err), zap.Any("IPs", ips), zap.String("action", action), zap.String("color", color))
 		return
 		return
 	}
 	}
 
 
@@ -316,13 +319,12 @@ func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string
 	// Publish the message
 	// Publish the message
 	err = s.mq.PublishWithCh(taskCfg.Exchange, routingKey, publishingMsg)
 	err = s.mq.PublishWithCh(taskCfg.Exchange, routingKey, publishingMsg)
 	if err != nil {
 	if err != nil {
-		s.logger.Error("发布 IP 白名单任务到 MQ 失败", zap.Error(err), zap.String("action", action),zap.String("color", color))
+		s.logger.Error("发布 IP 白名单任务到 MQ 失败", zap.Error(err), zap.String("action", action), zap.String("color", color))
 	} else {
 	} else {
-		s.logger.Info("成功将 IP 白名单任务发布到 MQ", zap.String("action", action),zap.String("color", color))
+		s.logger.Info("成功将 IP 白名单任务发布到 MQ", zap.String("action", action), zap.String("color", color))
 	}
 	}
 }
 }
 
 
-
 func (s *wafFormatterService) findIpDifferences(oldIps, newIps []string) ([]string, []string) {
 func (s *wafFormatterService) findIpDifferences(oldIps, newIps []string) ([]string, []string) {
 	// 使用 map 实现 set,用于快速查找
 	// 使用 map 实现 set,用于快速查找
 	oldIpsSet := make(map[string]struct{}, len(oldIps))
 	oldIpsSet := make(map[string]struct{}, len(oldIps))
@@ -366,7 +368,7 @@ func (s *wafFormatterService) WashDeleteWafIp(ctx context.Context, backendList [
 	return res, nil
 	return res, nil
 }
 }
 
 
-func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList []string,oldBackendList []string) ([]string, []string,error) {
+func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList []string, oldBackendList []string) ([]string, []string, error) {
 	var oldIps []string
 	var oldIps []string
 	var newIps []string
 	var newIps []string
 	for _, v := range oldBackendList {
 	for _, v := range oldBackendList {
@@ -387,12 +389,9 @@ func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList
 	}
 	}
 	addedIps, removedIps := s.findIpDifferences(oldIps, newIps)
 	addedIps, removedIps := s.findIpDifferences(oldIps, newIps)
 
 
-
-
-	return addedIps, removedIps , nil
+	return addedIps, removedIps, nil
 }
 }
 
 
-
 func (s *wafFormatterService) AddOrigin(ctx context.Context, req v1.WebJson) (int64, error) {
 func (s *wafFormatterService) AddOrigin(ctx context.Context, req v1.WebJson) (int64, error) {
 	ip, port, err := net.SplitHostPort(req.BackendList)
 	ip, port, err := net.SplitHostPort(req.BackendList)
 	if err != nil {
 	if err != nil {
@@ -401,14 +400,14 @@ func (s *wafFormatterService) AddOrigin(ctx context.Context, req v1.WebJson) (in
 	addr := v1.Addr{
 	addr := v1.Addr{
 		Protocol: req.ApiType,
 		Protocol: req.ApiType,
 		Host:     ip,
 		Host:     ip,
-		Port:  	  port,
+		Port:     port,
 	}
 	}
 	id, err := s.cdn.CreateOrigin(ctx, v1.Origin{
 	id, err := s.cdn.CreateOrigin(ctx, v1.Origin{
-		Addr: addr,
-		Weight: 10,
-		Description: req.Comment,
-		Host: req.Host,
-		IsOn: true,
+		Addr:                  addr,
+		Weight:                10,
+		Description:           req.Comment,
+		Host:                  req.Host,
+		IsOn:                  true,
 		TlsSecurityVerifyMode: "auto",
 		TlsSecurityVerifyMode: "auto",
 	})
 	})
 	if err != nil {
 	if err != nil {
@@ -453,7 +452,6 @@ func (s *wafFormatterService) WashDelIps(ctx context.Context, ips []string) ([]s
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-
 	// 2. 汇总所有计数结果
 	// 2. 汇总所有计数结果
 	totalCountMap := make(map[string]int)
 	totalCountMap := make(map[string]int)
 	// 将多个 for 循环合并到一个函数中,可以显得更整洁(可选)
 	// 将多个 for 循环合并到一个函数中,可以显得更整洁(可选)
@@ -478,7 +476,7 @@ func (s *wafFormatterService) WashDelIps(ctx context.Context, ips []string) ([]s
 }
 }
 
 
 // 判断域名是否为 中文域名,如果是,转换为 Punycode
 // 判断域名是否为 中文域名,如果是,转换为 Punycode
-func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context,domain string) (isIDN bool, punycodeDomain string, err error) {
+func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context, domain string) (isIDN bool, punycodeDomain string, err error) {
 	// 使用 idna.ToASCII 将域名转换为 Punycode。
 	// 使用 idna.ToASCII 将域名转换为 Punycode。
 	// 这个函数同时会根据 IDNA 规范验证域名的合法性。
 	// 这个函数同时会根据 IDNA 规范验证域名的合法性。
 	punycodeDomain, err = idna.ToASCII(domain)
 	punycodeDomain, err = idna.ToASCII(domain)
@@ -493,4 +491,44 @@ func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context,domain
 	isIDN = !strings.EqualFold(domain, punycodeDomain)
 	isIDN = !strings.EqualFold(domain, punycodeDomain)
 
 
 	return isIDN, punycodeDomain, nil
 	return isIDN, punycodeDomain, nil
-}
+}
+
+func (s *wafFormatterService) ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error) {
+	cert, err := tls.X509KeyPair([]byte(httpsCert), []byte(httpKey))
+	if err != nil {
+		return "", nil, nil, 0, 0, false, fmt.Errorf("无法从字符串加载密钥对: %v", err)
+	}
+
+	if len(cert.Certificate) == 0 {
+		return "", nil, nil, 0, 0, false, fmt.Errorf("提供的证书数据中没有找到证书。")
+	}
+
+	// 解析第一个证书(通常是叶子证书)
+	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
+	if err != nil {
+		return "", nil, nil, 0, 0, false, fmt.Errorf("无法解析证书: %v", err)
+	}
+
+	// 1. 获取 Common Name (通用名称)
+	// Common Name 位于 Subject 字段内. [1]
+	serverName = x509Cert.Subject.CommonName
+
+	// 2. 获取 DNS Names (备用主题名称中的DNS条目)
+	// DNS Names 直接是证书结构体的一个字段. [1]
+	DNSNames = x509Cert.DNSNames
+
+	// 检查证书是否为自签名
+	// 判断条件:颁发者(Issuer)和主题(Subject)相同,并且证书的签名可以由其自身的公钥验证
+	if err := x509Cert.CheckSignatureFrom(x509Cert); err == nil {
+		isSelfSigned = true
+	}
+
+	// 将CommonName放入一个切片,以匹配[]string的类型要求
+	var commonNames []string
+	if x509Cert.Subject.CommonName != "" {
+		commonNames = []string{x509Cert.Subject.CommonName}
+	}
+
+	return serverName, commonNames, DNSNames, x509Cert.NotBefore.Unix(), x509Cert.NotAfter.Unix(), isSelfSigned, nil
+
+}

+ 13 - 2
internal/service/webforwarding.go

@@ -248,16 +248,27 @@ func (s *webForwardingService) buildProxyJSONConfig(ctx context.Context, req *v1
 
 
 	// 判断协议类型,并处理 HTTPS 的特殊逻辑(证书)
 	// 判断协议类型,并处理 HTTPS 的特殊逻辑(证书)
 	if req.WebForwardingData.IsHttps == isHttps {
 	if req.WebForwardingData.IsHttps == isHttps {
-		apiType = protocolHttps
+		// 处理证书信息
+		serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.wafformatter.ParseCert(ctx, req.WebForwardingData.HttpsCert, req.WebForwardingData.HttpsKey)
+		if err != nil {
+			return nil, fmt.Errorf("解析证书失败: %w", err)
+		}
+
 		// 添加 SSL 证书
 		// 添加 SSL 证书
+
 		sslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
 		sslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
 			IsOn:         true,
 			IsOn:         true,
 			UserId:       int64(require.CdnUid),
 			UserId:       int64(require.CdnUid),
 			Name:         req.WebForwardingData.Domain,
 			Name:         req.WebForwardingData.Domain,
+			ServerName:   serverName,
 			Description:  req.WebForwardingData.Comment,
 			Description:  req.WebForwardingData.Comment,
 			CertData:     []byte(req.WebForwardingData.HttpsCert),
 			CertData:     []byte(req.WebForwardingData.HttpsCert),
 			KeyData:      []byte(req.WebForwardingData.HttpsKey),
 			KeyData:      []byte(req.WebForwardingData.HttpsKey),
-			IsSelfSigned: false,
+			TimeBeginAt:  before,
+			TimeEndAt:    after,
+			DnsNames:     DNSNames,
+			CommonNames:  commonNames,
+			IsSelfSigned: isSelfSigned,
 		})
 		})
 		if err != nil {
 		if err != nil {
 			return nil, fmt.Errorf("添加SSL证书失败: %w", err)
 			return nil, fmt.Errorf("添加SSL证书失败: %w", err)