zero-ppanel/apps/api/internal/middleware/decryptMiddleware.go
shanshanzhong e22a34ef3e
All checks were successful
Build docker and publish / prepare (20.15.1) (push) Successful in 12s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 1m30s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 1m55s
Build docker and publish / notify (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m13s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 5m30s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 1m32s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 1m36s
Build docker and publish / deploy (push) Successful in 44s
build: 优化 Dockerfile 构建缓存并移除 API 解密中间件调试日志
2026-03-01 20:30:28 -08:00

164 lines
3.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package middleware
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
"github.com/zero-ppanel/zero-ppanel/pkg/cryptox"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/rest/httpx"
)
type DecryptMiddleware struct {
conf config.Config
}
func NewDecryptMiddleware(c config.Config) *DecryptMiddleware {
return &DecryptMiddleware{conf: c}
}
func (m *DecryptMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !m.conf.Security.Enable {
next(w, r)
return
}
if r.Header.Get("Login-Type") != "device" {
next(w, r)
return
}
secret := m.conf.Security.SecuritySecret
rw := newEncryptResponseWriter(w, secret)
// 解密 GET query
query := r.URL.Query()
dataStr := query.Get("data")
timeStr := query.Get("time")
if dataStr != "" && timeStr != "" {
if plain, err := cryptox.Decrypt(dataStr, secret, timeStr); err == nil {
params := map[string]interface{}{}
if json.Unmarshal(plain, &params) == nil {
for k, v := range params {
query.Set(k, fmt.Sprintf("%v", v))
}
query.Del("data")
query.Del("time")
rawQuery := query.Encode()
if strings.Contains(r.RequestURI, "?") {
r.RequestURI = r.RequestURI[:strings.Index(r.RequestURI, "?")] + "?" + rawQuery
}
r.URL.RawQuery = rawQuery
}
}
}
// 解密 POST body
if r.Body != nil {
body, err := io.ReadAll(r.Body)
if err != nil || len(body) == 0 {
// body 为空或读取失败,直接放行
r.Body = io.NopCloser(bytes.NewBuffer(body))
next(rw, r)
rw.flush()
return
}
var envelope struct {
Data string `json:"data"`
Time string `json:"time"`
}
if err := json.Unmarshal(body, &envelope); err != nil || envelope.Data == "" {
httpx.Error(w, xerr.NewErrCode(xerr.DecryptFailed))
return
}
plain, err := cryptox.Decrypt(envelope.Data, secret, envelope.Time)
if err != nil {
httpx.Error(w, xerr.NewErrCode(xerr.DecryptFailed))
return
}
r.Body = io.NopCloser(bytes.NewBuffer(plain))
// 防止 httpx.Parse 内部 r.ParseForm() 消费已替换的 body。
// Go 标准库 ParseForm 首行检查 r.PostForm==nil 才读 body提前置空即可跳过。
r.PostForm = url.Values{}
r.ContentLength = int64(len(plain))
}
next(rw, r)
rw.flush()
}
}
// encryptResponseWriter 拦截响应,加密 data 字段
type encryptResponseWriter struct {
http.ResponseWriter
body *bytes.Buffer
secret string
status int
}
func newEncryptResponseWriter(w http.ResponseWriter, secret string) *encryptResponseWriter {
return &encryptResponseWriter{
ResponseWriter: w,
body: new(bytes.Buffer),
secret: secret,
status: http.StatusOK,
}
}
func (rw *encryptResponseWriter) WriteHeader(code int) {
rw.status = code
}
func (rw *encryptResponseWriter) Write(data []byte) (int, error) {
return rw.body.Write(data)
}
func (rw *encryptResponseWriter) WriteString(s string) (int, error) {
return rw.body.WriteString(s)
}
func (rw *encryptResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return rw.ResponseWriter.(http.Hijacker).Hijack()
}
func (rw *encryptResponseWriter) flush() {
buf := rw.body.Bytes()
out := buf
// 尝试加密 data 字段
params := map[string]interface{}{}
if err := json.Unmarshal(buf, &params); err == nil {
if data := params["data"]; data != nil {
var jsonData []byte
if str, ok := data.(string); ok {
jsonData = []byte(str)
} else {
jsonData, _ = json.Marshal(data)
}
if dataB64, nonce, err := cryptox.Encrypt(jsonData, rw.secret); err == nil {
params["data"] = map[string]interface{}{
"data": dataB64,
"time": nonce,
}
if enc, err := json.Marshal(params); err == nil {
out = enc
}
}
}
}
rw.ResponseWriter.WriteHeader(rw.status)
rw.ResponseWriter.Write(out)
}