Files
platform/web/globals/proxy.go

124 lines
2.7 KiB
Go

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"`
}