package globals import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base32" "encoding/base64" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) const ( PermitEndpoint = "/api/permit" ) var Proxy *ProxyClient type ProxyClient struct { } func initProxy() error { Proxy = &ProxyClient{} return nil } type ProxyPermitConfig struct { Id int32 `json:"id"` Whitelists *[]string `json:"whitelists,omitempty"` Username *string `json:"username,omitempty"` Password *string `json:"password,omitempty"` Expire time.Time `json:"expire"` } func (p *ProxyClient) Permit(host string, secret string, config []*ProxyPermitConfig) error { // 请求体加密 body, err := encrypt(config, secret) if err != nil { return fmt.Errorf("加密请求失败: %w", err) } //goland:noinspection HttpUrlsUsage resp, err := http.Post( fmt.Sprintf("http://%s:8848%s", host, PermitEndpoint), "application/json", strings.NewReader(body), ) if err != nil { return err } defer func(Body io.ReadCloser) { _ = Body.Close() }(resp.Body) if resp.StatusCode != http.StatusOK { return fmt.Errorf("配置端口许可失败: %s", resp.Status) } return nil } func encrypt(req []*ProxyPermitConfig, secretStr string) (string, error) { var encoding = base32.StdEncoding.WithPadding(base32.NoPadding) // 创建 AES 密钥 secret, err := encoding.DecodeString(secretStr) if err != nil { return "", fmt.Errorf("解码 AES 密钥字符串失败: %w", err) } block, err := aes.NewCipher(secret) if err != nil { return "", fmt.Errorf("创建 AES 密钥失败: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { return "", fmt.Errorf("创建 AES GCM 失败: %w", err) } // 加密内容 bytes, err := json.Marshal(req) if err != nil { return "", fmt.Errorf("配置参数序列化失败: %w", err) } nonceBytes := make([]byte, gcm.NonceSize()) if _, err := rand.Read(nonceBytes); err != nil { return "", fmt.Errorf("生成随机数失败: %w", err) } nonce := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(nonceBytes) timestamp := time.Now().UnixMilli() aad := fmt.Sprintf("%s:%d", nonce, timestamp) ciphertext := gcm.Seal(nil, nonceBytes, bytes, []byte(aad)) encoded := base64.StdEncoding.EncodeToString(ciphertext) // 生成请求体 encrypted := EncryptReq{ Content: encoded, Nonce: nonce, Timestamp: timestamp, } body, err := json.Marshal(encrypted) if err != nil { return "", fmt.Errorf("请求参数序列化失败: %w", err) } return string(body), nil } type EncryptReq struct { Content string `json:"content"` Nonce string `json:"nonce"` Timestamp int64 `json:"timestamp"` }