123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- package service
- import (
- "context"
- "crypto/tls"
- "crypto/x509"
- "encoding/json"
- "fmt"
- v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
- "github.com/go-nunu/nunu-layout-advanced/internal/repository"
- )
- type SslCertService interface {
- ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error)
- AddSSLCert(ctx context.Context, req v1.SSL) (int64, error)
- EditSSLCert(ctx context.Context, req v1.SSL) error
- AddSslPolicy(ctx context.Context, CertIds []int64) (sslPolicyId int64, err error)
- EditSslPolicy(ctx context.Context, sslPolicyId int64, CertIds []int64, action string) error
- }
- func NewSslCertService(
- service *Service,
- webForwardingRep repository.WebForwardingRepository,
- cdn CdnService,
- ) SslCertService {
- return &sslCertService{
- Service: service,
- webForwardingRep: webForwardingRep,
- cdn: cdn,
- }
- }
- type sslCertService struct {
- *Service
- webForwardingRep repository.WebForwardingRepository
- cdn CdnService
- }
- func (s *sslCertService) 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
- }
- func (s *sslCertService) AddSslPolicy(ctx context.Context, CertIds []int64) (sslPolicyId int64, err error) {
- // 构造策略中引用的证书列表
- type sslCerts struct {
- IsOn bool `json:"isOn" form:"isOn"`
- CertId int64 `json:"certId" form:"certId"`
- }
- var sslCertsSlice []sslCerts
- for _, certId := range CertIds {
- sslCertsSlice = append(sslCertsSlice, sslCerts{
- IsOn: true,
- CertId: certId,
- })
- }
- sslCertsJson, err := json.Marshal(sslCertsSlice)
- if err != nil {
- return 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, fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
- }
- return newSslPolicyId, nil
- }
- func (s *sslCertService) EditSslPolicy(ctx context.Context, sslPolicyId int64, CertIds []int64, action string) error {
- type sslCerts struct {
- IsOn bool `json:"isOn" form:"isOn"`
- CertId int64 `json:"certId" form:"certId"`
- }
- oldCertIds, err := s.webForwardingRep.GetSslCertId(ctx, sslPolicyId)
- if err != nil {
- return fmt.Errorf("获取SSL证书失败: %w", err)
- }
- var sslCertsSlice []sslCerts
- newCertIdSet := make(map[int64]struct{}, len(CertIds))
- for _, certId := range CertIds {
- newCertIdSet[certId] = struct{}{}
- }
- oldCertIdSet := make(map[int64]struct{}, len(oldCertIds))
- for _, oldCert := range oldCertIds {
- oldCertIdSet[oldCert.CertId] = struct{}{}
- }
- switch action {
- case "add":
- for _, certId := range CertIds {
- // 使用 oldCertIdSet 进行 O(1) 复杂度的查找。
- if _, found := oldCertIdSet[certId]; !found {
- // 如果在旧的集合中没找到,说明是新增的。
- sslCertsSlice = append(sslCertsSlice, sslCerts{
- IsOn: true,
- CertId: certId,
- })
- }
- }
- case "del":
- for _, oldCert := range oldCertIds {
- // 使用 newCertIdSet 进行 O(1) 复杂度的查找。
- if _, found := newCertIdSet[oldCert.CertId]; !found {
- // 如果在新的集合中没找到,说明被删除了。
- sslCertsSlice = append(sslCertsSlice, sslCerts{
- IsOn: false,
- CertId: oldCert.CertId,
- })
- }
- }
- }
- sslCertsJson, err := json.Marshal(sslCertsSlice)
- if err != nil {
- return fmt.Errorf("序列化SSL证书引用失败: %w", err)
- }
- // 调用CDN服务创建策略
- err = s.cdn.EditSSLPolicy(ctx, v1.SSLPolicy{
- SslPolicyId: sslPolicyId,
- Http2Enabled: true,
- SslCertsJSON: sslCertsJson,
- MinVersion: "TLS 1.1", // 可根据安全要求调整
- })
- if err != nil {
- // 如果策略创建失败,需要考虑回滚或记录错误,这里直接返回错误
- return fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
- }
- return nil
- }
- func (s *sslCertService) AddSSLCert(ctx context.Context, req v1.SSL) (int64, error) {
- // 1. 解析证书文件,提取元数据
- serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
- if err != nil {
- return 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, fmt.Errorf("添加SSL证书到CDN失败: %w", err)
- }
- return newSslCertId, nil
- }
- func (s *sslCertService) EditSSLCert(ctx context.Context, req v1.SSL) error {
- if req.CertData == "" && req.KeyData == "" {
- return nil
- }
- // 1. 解析证书文件,提取元数据
- serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
- if err != nil {
- return fmt.Errorf("解析证书失败: %w", err)
- }
- // 2. 将证书添加到CDN提供商
- // 这是获取可以在策略中引用的 `sslCertId` 的前提
- err = s.cdn.EditSSLCert(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 fmt.Errorf("添加SSL证书到CDN失败: %w", err)
- }
- return nil
- }
|