feat(subscribe): add short token generation and validation logic
This commit is contained in:
parent
8a4cfcbdb3
commit
e3999ba75f
@ -470,6 +470,7 @@ type (
|
||||
Upload int64 `json:"upload"`
|
||||
Token string `json:"token"`
|
||||
Status uint8 `json:"status"`
|
||||
Short string `json:"short"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
@ -23,6 +23,22 @@ func SubscribeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
ua := c.GetHeader("User-Agent")
|
||||
req.UA = c.Request.Header.Get("User-Agent")
|
||||
req.Flag = c.Query("flag")
|
||||
if svcCtx.Config.Subscribe.PanDomain {
|
||||
domain := c.Request.Host
|
||||
domainArr := strings.Split(domain, ".")
|
||||
short, err := tool.FixedUniqueString(req.Token, 8, "")
|
||||
if err != nil {
|
||||
logger.Errorf("[SubscribeHandler] Generate short token failed: %v", err)
|
||||
c.String(http.StatusInternalServerError, "Internal Server")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if short != domainArr[0] {
|
||||
c.String(http.StatusForbidden, "Access denied")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if svcCtx.Config.Subscribe.UserAgentLimit {
|
||||
if ua == "" {
|
||||
|
||||
@ -60,6 +60,8 @@ func (l *QueryUserSubscribeLogic) QueryUserSubscribe() (resp *types.QueryUserSub
|
||||
}
|
||||
}
|
||||
|
||||
short, _ := tool.FixedUniqueString(item.Token, 8, "")
|
||||
sub.Short = short
|
||||
sub.ResetTime = calculateNextResetTime(&sub)
|
||||
resp.List = append(resp.List, sub)
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ func initServer(svc *svc.ServiceContext) *gin.Engine {
|
||||
}
|
||||
r.Use(sessions.Sessions("ppanel", sessionStore))
|
||||
// use cors middleware
|
||||
r.Use(middleware.TraceMiddleware(svc), middleware.LoggerMiddleware(svc), middleware.CorsMiddleware, middleware.PanDomainMiddleware(svc), gin.Recovery())
|
||||
r.Use(middleware.TraceMiddleware(svc), middleware.LoggerMiddleware(svc), middleware.CorsMiddleware, gin.Recovery())
|
||||
|
||||
// register handlers
|
||||
handler.RegisterHandlers(r, svc)
|
||||
|
||||
@ -2597,6 +2597,7 @@ type UserSubscribe struct {
|
||||
Upload int64 `json:"upload"`
|
||||
Token string `json:"token"`
|
||||
Status uint8 `json:"status"`
|
||||
Short string `json:"short"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
38
pkg/tool/string.go
Normal file
38
pkg/tool/string.go
Normal file
@ -0,0 +1,38 @@
|
||||
package tool
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func FixedUniqueString(s string, length int, alphabet string) (string, error) {
|
||||
if alphabet == "" {
|
||||
alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
}
|
||||
if length <= 0 {
|
||||
return "", errors.New("length must be > 0")
|
||||
}
|
||||
if length > len(alphabet) {
|
||||
return "", errors.New("length greater than available unique characters")
|
||||
}
|
||||
|
||||
// Generate deterministic seed from SHA256
|
||||
hash := sha256.Sum256([]byte(s))
|
||||
seed := int64(binary.LittleEndian.Uint64(hash[:8])) // 前 8 字节
|
||||
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
|
||||
// Copy alphabet to mutable array
|
||||
data := []rune(alphabet)
|
||||
|
||||
// Deterministic shuffle (Fisher–Yates)
|
||||
for i := len(data) - 1; i > 0; i-- {
|
||||
j := r.Intn(i + 1)
|
||||
data[i], data[j] = data[j], data[i]
|
||||
}
|
||||
|
||||
// Take first N characters
|
||||
return string(data[:length]), nil
|
||||
}
|
||||
27
pkg/tool/string_test.go
Normal file
27
pkg/tool/string_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
package tool
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFixedUniqueString(t *testing.T) {
|
||||
a := "example"
|
||||
b := "example1"
|
||||
c := "example"
|
||||
|
||||
strA1, err := FixedUniqueString(a, 8, "")
|
||||
strB1, err := FixedUniqueString(b, 8, "")
|
||||
strC1, err := FixedUniqueString(c, 8, "")
|
||||
if err != nil {
|
||||
t.Logf("Error: %v", err.Error())
|
||||
return
|
||||
}
|
||||
if strA1 != strC1 {
|
||||
t.Errorf("Expected strA1 and strC1 to be equal, got %s and %s", strA1, strC1)
|
||||
}
|
||||
if strA1 == strB1 {
|
||||
t.Errorf("Expected strA1 and strB1 to be different, got %s and %s", strA1, strB1)
|
||||
}
|
||||
t.Logf("strA1 and strB1 are not equal, strA1: %s, strB1: %s", strA1, strB1)
|
||||
t.Logf("strA1 and strC1 are equal,strA1: %s, strC1: %s", strA1, strC1)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user