package jwtx import ( "context" "sync" "time" "github.com/golang-jwt/jwt/v5" ) // ================== // 内部管理器 // ================== type jwtManager struct { secret string tokenExpiry time.Duration } var ( manager *jwtManager once sync.Once ) // ================== // 初始化 // ================== // Init 初始化(只执行一次) // expiry = -1 表示不过期 func Init(secret string, expiry time.Duration) { once.Do(func() { manager = &jwtManager{ secret: secret, tokenExpiry: expiry, } }) } func getManager() *jwtManager { if manager == nil { panic("jwt未初始化,请先调用Init") } return manager } // ================== // Claims(通用结构) // ================== type TokenData struct { UserID int64 `json:"user_id"` } type CustomClaims struct { Data TokenData `json:"data"` jwt.RegisteredClaims } // ================== // 对外方法 // ================== // CreateToken 生成 JWT func CreateToken(ctx context.Context, data TokenData) (string, error) { m := getManager() return m.createToken(data) } // VerifyToken 校验 token 并返回 claims func VerifyToken(ctx context.Context, tokenString string) (*CustomClaims, bool) { if tokenString == "" { return nil, false } m := getManager() token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(m.secret), nil }) if err != nil || !token.Valid { return nil, false } claims, ok := token.Claims.(*CustomClaims) if !ok { return nil, false } return claims, true } // ================== // 内部实现 // ================== func (j *jwtManager) createToken(data TokenData) (string, error) { now := time.Now() claims := &CustomClaims{ Data: data, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), }, } if j.tokenExpiry > 0 { claims.ExpiresAt = jwt.NewNumericDate(now.Add(j.tokenExpiry)) } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(j.secret)) }