hi-server/internal/middleware/signatureMiddleware.go
shanshanzhong 7308aa9191
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 7m37s
无订阅 支付后出现两个订阅
2026-03-05 21:53:36 -08:00

138 lines
3.1 KiB
Go

package middleware
import (
"bytes"
"io"
"strings"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
"github.com/perfect-panel/server/pkg/signature"
"github.com/perfect-panel/server/pkg/xerr"
)
var (
publicSignaturePrefixes = []string{
"/v1/auth",
"/v1/auth/oauth",
"/v1/common",
"/v1/public",
}
defaultSignatureSkipPrefixes = []string{
"/v1/notify/",
"/v1/iap/notifications",
"/v1/telegram/webhook",
"/v1/subscribe/config",
}
)
func SignatureMiddleware(srvCtx *svc.ServiceContext) func(c *gin.Context) {
skipPrefixes := collectSignatureSkipPrefixes(srvCtx)
return func(c *gin.Context) {
path := c.Request.URL.Path
if !isPublicSignaturePath(path) {
c.Next()
return
}
for _, prefix := range skipPrefixes {
if strings.HasPrefix(path, prefix) {
c.Next()
return
}
}
if !srvCtx.Config.Signature.EnableSignature {
c.Next()
return
}
if c.GetHeader("X-Signature-Enabled") != "1" {
c.Next()
return
}
appId := c.GetHeader("X-App-Id")
if appId == "" {
result.HttpResult(c, nil, xerr.NewErrCode(xerr.InvalidAccess))
c.Abort()
return
}
timestamp := c.GetHeader("X-Timestamp")
nonce := c.GetHeader("X-Nonce")
sig := c.GetHeader("X-Signature")
if timestamp == "" || nonce == "" || sig == "" {
result.HttpResult(c, nil, xerr.NewErrCode(xerr.SignatureMissing))
c.Abort()
return
}
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes, _ = io.ReadAll(c.Request.Body)
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
sts := signature.BuildStringToSign(
c.Request.Method,
path,
c.Request.URL.RawQuery,
bodyBytes,
appId,
timestamp,
nonce,
)
if err := srvCtx.SignatureValidator.Validate(c.Request.Context(), appId, timestamp, nonce, sig, sts); err != nil {
result.HttpResult(c, nil, xerr.NewErrCode(mapSignatureErr(err)))
c.Abort()
return
}
c.Next()
}
}
func isPublicSignaturePath(path string) bool {
for _, prefix := range publicSignaturePrefixes {
if strings.HasPrefix(path, prefix) {
return true
}
}
return false
}
func collectSignatureSkipPrefixes(srvCtx *svc.ServiceContext) []string {
prefixSet := make(map[string]struct{}, len(defaultSignatureSkipPrefixes)+len(srvCtx.Config.AppSignature.SkipPrefixes)+1)
for _, prefix := range defaultSignatureSkipPrefixes {
prefixSet[prefix] = struct{}{}
}
for _, prefix := range srvCtx.Config.AppSignature.SkipPrefixes {
if strings.TrimSpace(prefix) == "" {
continue
}
prefixSet[prefix] = struct{}{}
}
if path := strings.TrimSpace(srvCtx.Config.Subscribe.SubscribePath); path != "" {
prefixSet[path] = struct{}{}
}
prefixes := make([]string, 0, len(prefixSet))
for prefix := range prefixSet {
prefixes = append(prefixes, prefix)
}
return prefixes
}
func mapSignatureErr(err error) uint32 {
switch err {
case signature.ErrSignatureMissing:
return xerr.SignatureMissing
case signature.ErrSignatureExpired:
return xerr.SignatureExpired
case signature.ErrSignatureReplay:
return xerr.SignatureReplay
default:
return xerr.SignatureInvalid
}
}