package jwt import ( "base-go-v2/internal/config" "errors" "github.com/golang-jwt/jwt/v5" "time" ) var ( ErrTokenExpired = errors.New("token expired") ErrTokenInvalid = errors.New("token invalid") ) // CustomClaims 定义自己的 payload 结构 type CustomClaims struct { UserID string `json:"user_id"` jwt.RegisteredClaims } // CreateToken 创建一个 JWT token // 支持 config.App.JWT.Expiry == -1 永不过期 func CreateToken(orgID, userID string) (string, error) { claims := CustomClaims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), }, } expiry := config.App.JWT.Expiry switch { case expiry > 0: expireAt := time.Now().Add(expiry) claims.ExpiresAt = jwt.NewNumericDate(expireAt) case expiry == -1: // 永不过期,ExpiresAt 设置为 nil claims.ExpiresAt = nil default: // 默认过期时间,例如 1 小时 expireAt := time.Now().Add(time.Hour) claims.ExpiresAt = jwt.NewNumericDate(expireAt) } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(config.App.JWT.Secret)) } // VerifyToken 验证并解析 JWT token func VerifyToken(tokenString string) (*CustomClaims, error) { signKey := []byte(config.App.JWT.Secret) token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { // 校验签名算法 if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, ErrTokenInvalid } return signKey, nil }) if err != nil { return nil, ErrTokenInvalid } claims, ok := token.Claims.(*CustomClaims) if !ok || !token.Valid { return nil, ErrTokenInvalid } // 检查过期:ExpiresAt 为 nil 时永不过期 if claims.ExpiresAt != nil && time.Now().After(claims.ExpiresAt.Time) { return nil, ErrTokenExpired } return claims, nil }