diff --git a/go.mod b/go.mod index 93a22a2c..0c084f43 100644 --- a/go.mod +++ b/go.mod @@ -18,11 +18,12 @@ require ( github.com/fatih/color v1.18.0 github.com/fsnotify/fsnotify v1.8.0 github.com/gin-gonic/gin v1.10.0 - github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/golang-jwt/jwt/v4 v4.5.2 github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 github.com/joho/godotenv v1.5.1 github.com/json-iterator/go v1.1.12 + github.com/mozillazg/go-pinyin v0.20.0 github.com/pkoukk/tiktoken-go v0.1.7 github.com/rhysd/go-github-selfupdate v1.2.3 github.com/spf13/cast v1.7.1 @@ -98,7 +99,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/montanaflynn/stats v0.7.1 // indirect - github.com/mozillazg/go-pinyin v0.20.0 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index c2224569..944bbda5 100644 --- a/go.sum +++ b/go.sum @@ -101,8 +101,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= diff --git a/helper/jwt.go b/helper/jwt.go index 60531a83..2d7083da 100644 --- a/helper/jwt.go +++ b/helper/jwt.go @@ -2,9 +2,10 @@ package helper import ( "fmt" + "strings" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" @@ -13,12 +14,19 @@ import ( "github.com/yaoapp/yao/config" ) +const ( + // MaxTokenLength is the maximum allowed length for a JWT token + MaxTokenLength = 4096 + // MaxTokenParts is the maximum allowed number of parts in a JWT token (header.payload.signature) + MaxTokenParts = 3 +) + // JwtClaims 用户Token type JwtClaims struct { ID int `json:"id"` SID string `json:"sid"` Data map[string]interface{} `json:"data"` - jwt.StandardClaims + jwt.RegisteredClaims } // JwtToken JWT令牌 @@ -29,6 +37,18 @@ type JwtToken struct { // JwtValidate JWT 校验 func JwtValidate(tokenString string, secret ...[]byte) *JwtClaims { + // Check token length + if len(tokenString) > MaxTokenLength { + exception.New("Token too long", 401).Throw() + return nil + } + + // Check number of parts + parts := strings.Split(tokenString, ".") + if len(parts) > MaxTokenParts { + exception.New("Invalid token format", 401).Throw() + return nil + } jwtSecret := []byte(config.Conf.JWTSecret) if len(secret) > 0 { @@ -62,12 +82,12 @@ func JwtMake(id int, data map[string]interface{}, option map[string]interface{}, jwtSecret = secret[0] } - now := time.Now().Unix() + now := time.Now() sid := "" - timeout := int64(3600) + timeout := time.Hour uid := fmt.Sprintf("%d", id) subject := "User Token" - audience := "Yao Process utils.jwt.Make" + audience := []string{"Yao Process utils.jwt.Make"} issuer := fmt.Sprintf("xiang:%d", id) if v, has := option["subject"]; has { @@ -75,7 +95,7 @@ func JwtMake(id int, data map[string]interface{}, option map[string]interface{}, } if v, has := option["audience"]; has { - audience = fmt.Sprintf("%v", v) + audience = []string{fmt.Sprintf("%v", v)} } if v, has := option["issuer"]; has { @@ -87,45 +107,42 @@ func JwtMake(id int, data map[string]interface{}, option map[string]interface{}, } if v, has := option["timeout"]; has { - timeout = int64(any.Of(v).CInt()) + timeout = time.Duration(any.Of(v).CInt()) * time.Second } - expiresAt := now + timeout + expiresAt := now.Add(timeout) if v, has := option["expires_at"]; has { - expiresAt = int64(any.Of(v).CInt()) + expiresAt = time.Unix(int64(any.Of(v).CInt()), 0) } if sid == "" { sid = session.ID() } - // 设定会话过期时间 (并写需要加锁,这个逻辑需要优化) - // session.Global().Expire(time.Duration(timeout) * time.Second) - claims := &JwtClaims{ ID: id, SID: sid, // 会话ID Data: data, - StandardClaims: jwt.StandardClaims{ - Id: uid, // 唯一ID - Subject: subject, // 主题 - Audience: audience, // 接收人 - ExpiresAt: expiresAt, // 过期时间 - NotBefore: now, // 生效时间 - IssuedAt: now, // 签发时间 - Issuer: issuer, // 签发人 + RegisteredClaims: jwt.RegisteredClaims{ + ID: uid, // 唯一ID + Subject: subject, // 主题 + Audience: audience, // 接收人 + ExpiresAt: jwt.NewNumericDate(expiresAt), // 过期时间 + NotBefore: jwt.NewNumericDate(now), // 生效时间 + IssuedAt: jwt.NewNumericDate(now), // 签发时间 + Issuer: issuer, // 签发人 }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString([]byte(jwtSecret)) + tokenString, err := token.SignedString(jwtSecret) if err != nil { exception.New("JWT Make Error: %s", 500, err.Error()).Throw() } return JwtToken{ Token: tokenString, - ExpiresAt: expiresAt, + ExpiresAt: expiresAt.Unix(), } }