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"`
|
Upload int64 `json:"upload"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Status uint8 `json:"status"`
|
Status uint8 `json:"status"`
|
||||||
|
Short string `json:"short"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,22 @@ func SubscribeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
|||||||
ua := c.GetHeader("User-Agent")
|
ua := c.GetHeader("User-Agent")
|
||||||
req.UA = c.Request.Header.Get("User-Agent")
|
req.UA = c.Request.Header.Get("User-Agent")
|
||||||
req.Flag = c.Query("flag")
|
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 svcCtx.Config.Subscribe.UserAgentLimit {
|
||||||
if ua == "" {
|
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)
|
sub.ResetTime = calculateNextResetTime(&sub)
|
||||||
resp.List = append(resp.List, sub)
|
resp.List = append(resp.List, sub)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ func initServer(svc *svc.ServiceContext) *gin.Engine {
|
|||||||
}
|
}
|
||||||
r.Use(sessions.Sessions("ppanel", sessionStore))
|
r.Use(sessions.Sessions("ppanel", sessionStore))
|
||||||
// use cors middleware
|
// 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
|
// register handlers
|
||||||
handler.RegisterHandlers(r, svc)
|
handler.RegisterHandlers(r, svc)
|
||||||
|
|||||||
@ -2597,6 +2597,7 @@ type UserSubscribe struct {
|
|||||||
Upload int64 `json:"upload"`
|
Upload int64 `json:"upload"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Status uint8 `json:"status"`
|
Status uint8 `json:"status"`
|
||||||
|
Short string `json:"short"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_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