JWT Complete Guide - zhoudm1743/go-util GitHub Wiki

JWT (JSON Web Token) 是 Go-Util 的企业级认证独立包,零依赖实现,支持多种签名算法。

🚀 特性亮点

  • 🔐 零依赖: 仅使用 Go 标准库,无第三方依赖
  • 🎯 多算法支持: HMAC (HS256/384/512) 和 RSA (RS256/384/512)
  • ⚡ 高性能: 10万次/秒 HMAC 签名验证,1万次/秒 RSA
  • 🛡️ 安全: 完整的验证和错误处理
  • 🔧 易用: 链式 API 和便捷函数
  • 🏗️ 灵活: 通用 API 设计,算法参数化

📦 安装

go get github.com/zhoudm1743/go-util/jwt

⚡ 快速开始

import "github.com/zhoudm1743/go-util/jwt"

// 生成 JWT
secret := []byte("your-secret-key")
claims := jwt.MapClaims{
    "sub": "user123",
    "name": "张三",
    "exp": time.Now().Add(24 * time.Hour).Unix(),
}

token, err := jwt.GenerateHS256(secret, claims)

// 验证 JWT
parsed, err := jwt.ParseHS256(token, secret)
if err == nil && parsed.Valid {
    fmt.Println("验证成功!")
}

🎯 核心概念

JWT 结构

JWT 由三部分组成,用点号分隔:

Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  1. Header - 算法和令牌类型
  2. Payload - 声明信息 (Claims)
  3. Signature - 签名,确保数据完整性

声明 (Claims)

JWT 支持标准声明和自定义声明:

// 标准声明
type StandardClaims struct {
    Issuer    string `json:"iss,omitempty"` // 签发者
    Subject   string `json:"sub,omitempty"` // 主题
    Audience  string `json:"aud,omitempty"` // 受众
    ExpiresAt int64  `json:"exp,omitempty"` // 过期时间
    NotBefore int64  `json:"nbf,omitempty"` // 生效时间
    IssuedAt  int64  `json:"iat,omitempty"` // 签发时间
    ID        string `json:"jti,omitempty"` // JWT ID
}

// 自定义声明
claims := jwt.MapClaims{
    "sub":  "user123",
    "name": "张三",
    "role": "admin",
    "permissions": []string{"read", "write"},
    "exp": time.Now().Add(24 * time.Hour).Unix(),
}

🔧 通用 API (推荐)

基础用法

通用 API 让算法切换更容易:

// 支持的算法
algorithms := []jwt.SigningMethod{
    jwt.SigningMethodHS256,  // HMAC SHA-256
    jwt.SigningMethodHS384,  // HMAC SHA-384
    jwt.SigningMethodHS512,  // HMAC SHA-512
    jwt.SigningMethodRS256,  // RSA SHA-256
    jwt.SigningMethodRS384,  // RSA SHA-384
    jwt.SigningMethodRS512,  // RSA SHA-512
}

secret := []byte("your-secret-key")
claims := jwt.MapClaims{
    "sub": "user123",
    "exp": time.Now().Add(time.Hour).Unix(),
}

// 使用任意算法生成
for _, method := range algorithms[:3] { // HMAC 算法
    token, err := jwt.Generate(method, secret, claims)
    if err != nil {
        log.Printf("生成失败: %v", err)
        continue
    }
    
    // 验证令牌
    parsed, err := jwt.Parse(method, token, secret)
    if err != nil {
        log.Printf("验证失败: %v", err)
        continue
    }
    
    fmt.Printf("算法 %s: 生成和验证成功\n", method.Name())
}

自定义声明类型

// 定义自定义声明结构
type UserClaims struct {
    jwt.StandardClaims
    UserID      int      `json:"user_id"`
    Username    string   `json:"username"`
    Role        string   `json:"role"`
    Permissions []string `json:"permissions"`
}

// 生成带自定义声明的 JWT
userClaims := &UserClaims{
    StandardClaims: jwt.StandardClaims{
        Subject:   "user123",
        ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
        IssuedAt:  time.Now().Unix(),
        Issuer:    "go-util-app",
    },
    UserID:      123,
    Username:    "zhangsan",
    Role:        "admin",
    Permissions: []string{"read", "write", "delete"},
}

token, err := jwt.Generate(jwt.SigningMethodHS256, secret, userClaims)

// 解析自定义声明
var parsedClaims UserClaims
parsed, err := jwt.ParseWithClaims(jwt.SigningMethodHS256, token, secret, &parsedClaims)
if err == nil {
    fmt.Printf("用户: %s, 角色: %s\n", parsedClaims.Username, parsedClaims.Role)
}

🏗️ 构建器模式

构建器模式提供更流畅的 API:

// 基础构建器使用
token, err := jwt.NewBuilder(jwt.SigningMethodHS256, secret).
    SetIssuer("go-util-app").
    SetSubject("user123").
    SetAudience("web-app").
    SetExpirationFromNow(24 * time.Hour).
    SetNotBeforeFromNow(0).  // 立即生效
    SetIssuedAtNow().
    SetClaim("role", "admin").
    SetClaim("permissions", []string{"read", "write"}).
    Build()

// 高级构建器使用
builder := jwt.NewBuilder(jwt.SigningMethodHS512, secret)

// 批量设置声明
userInfo := map[string]interface{}{
    "user_id":   123,
    "username":  "zhangsan",
    "email":     "[email protected]",
    "role":      "admin",
    "department": "IT",
}

token, err := builder.
    SetIssuer("company-auth-service").
    SetSubject(fmt.Sprintf("user_%d", userInfo["user_id"])).
    SetExpirationFromNow(8 * time.Hour).  // 8小时工作时间
    SetClaims(userInfo).
    Build()

🔐 HMAC 算法

HMAC 算法使用共享密钥,适合单体应用或内部服务:

// 生成强密钥
secret := jwt.GenerateHMACSecret(32)  // 32字节 = 256位

// HS256 - 最常用
token, err := jwt.GenerateHS256(secret, claims)
parsed, err := jwt.ParseHS256(token, secret)

// HS384 - 更高安全性
token, err := jwt.GenerateHS384(secret, claims)
parsed, err := jwt.ParseHS384(token, secret)

// HS512 - 最高安全性
token, err := jwt.GenerateHS512(secret, claims)
parsed, err := jwt.ParseHS512(token, secret)

// 密钥轮换示例
type KeyManager struct {
    currentKey []byte
    oldKeys    [][]byte
}

func (km *KeyManager) Sign(claims jwt.Claims) (string, error) {
    return jwt.GenerateHS256(km.currentKey, claims)
}

func (km *KeyManager) Verify(tokenString string) (*jwt.Token, error) {
    // 先尝试当前密钥
    if token, err := jwt.ParseHS256(tokenString, km.currentKey); err == nil {
        return token, nil
    }
    
    // 尝试旧密钥
    for _, oldKey := range km.oldKeys {
        if token, err := jwt.ParseHS256(tokenString, oldKey); err == nil {
            return token, nil
        }
    }
    
    return nil, errors.New("无效的令牌")
}

🔑 RSA 算法

RSA 算法使用公私钥对,适合分布式系统:

// 生成 RSA 密钥对
privateKey, publicKey, err := jwt.GenerateRSAKeyPair(2048)
if err != nil {
    log.Fatal(err)
}

// 使用私钥签名
token, err := jwt.GenerateRS256(privateKey, claims)

// 使用公钥验证
parsed, err := jwt.ParseRS256(token, publicKey)

// PEM 格式密钥处理
privateKeyPEM := jwt.EncodeRSAPrivateKeyToPEM(privateKey)
publicKeyPEM := jwt.EncodeRSAPublicKeyToPEM(publicKey)

// 保存到文件
err = os.WriteFile("private.pem", privateKeyPEM, 0600)
err = os.WriteFile("public.pem", publicKeyPEM, 0644)

// 从文件加载
privateKeyData, _ := os.ReadFile("private.pem")
publicKeyData, _ := os.ReadFile("public.pem")

privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyData)
publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyData)

// 分布式验证示例
type AuthService struct {
    privateKey *rsa.PrivateKey
}

type APIService struct {
    publicKey *rsa.PublicKey
}

func (as *AuthService) GenerateToken(userID string) (string, error) {
    claims := jwt.MapClaims{
        "sub": userID,
        "iss": "auth-service",
        "exp": time.Now().Add(time.Hour).Unix(),
    }
    return jwt.GenerateRS256(as.privateKey, claims)
}

func (api *APIService) ValidateToken(tokenString string) (*jwt.Token, error) {
    return jwt.ParseRS256(tokenString, api.publicKey)
}

🔧 实用工具

令牌解析和验证

tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

// 解码头部(不验证签名)
header, err := jwt.DecodeHeader(tokenString)
if err == nil {
    fmt.Printf("算法: %s, 类型: %s\n", header.Algorithm, header.Type)
}

// 解码声明(不验证签名)
var claims jwt.MapClaims
err = jwt.DecodeClaims(tokenString, &claims)
if err == nil {
    fmt.Printf("主题: %s\n", claims["sub"])
}

// 检查令牌是否过期
if exp, ok := claims["exp"].(float64); ok {
    if jwt.IsExpired(int64(exp)) {
        fmt.Println("令牌已过期")
    }
}

// 验证标准声明
standardClaims := &jwt.StandardClaims{
    ExpiresAt: time.Now().Add(time.Hour).Unix(),
    NotBefore: time.Now().Unix(),
    IssuedAt:  time.Now().Unix(),
}

if err := jwt.ValidateStandardClaims(standardClaims); err != nil {
    fmt.Printf("声明验证失败: %v", err)
}

时间处理工具

// 时间戳转换
now := time.Now()
timestamp := jwt.TimeToUnix(now)
backToTime := jwt.UnixToTime(timestamp)

// 便捷时间设置
claims := jwt.MapClaims{
    "exp": jwt.ExpiresAt(time.Hour),     // 1小时后过期
    "nbf": jwt.NotBefore(time.Minute),   // 1分钟后生效
    "iat": jwt.IssuedAtNow(),            // 当前时间签发
}

// 时间范围验证
isValid := jwt.IsValidTimeRange(
    claims["nbf"].(int64),  // 生效时间
    claims["exp"].(int64),  // 过期时间
)

🌐 Web 框架集成

Gin 中间件

func JWTMiddleware(secret []byte) gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.JSON(401, gin.H{"error": "缺少授权头"})
            c.Abort()
            return
        }
        
        // 解析 "Bearer <token>"
        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        
        token, err := jwt.ParseHS256(tokenString, secret)
        if err != nil || !token.Valid {
            c.JSON(401, gin.H{"error": "无效令牌"})
            c.Abort()
            return
        }
        
        // 将用户信息存储到上下文
        if claims, ok := token.Claims.(jwt.MapClaims); ok {
            c.Set("user_id", claims["sub"])
            c.Set("role", claims["role"])
        }
        
        c.Next()
    }
}

// 使用中间件
r := gin.Default()
r.Use(JWTMiddleware(secret))

r.GET("/protected", func(c *gin.Context) {
    userID := c.GetString("user_id")
    role := c.GetString("role")
    c.JSON(200, gin.H{
        "message": "访问成功",
        "user_id": userID,
        "role":    role,
    })
})

Echo 中间件

func JWTMiddleware(secret []byte) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            authHeader := c.Request().Header.Get("Authorization")
            if authHeader == "" {
                return echo.NewHTTPError(401, "缺少授权头")
            }
            
            tokenString := strings.TrimPrefix(authHeader, "Bearer ")
            
            token, err := jwt.ParseHS256(tokenString, secret)
            if err != nil || !token.Valid {
                return echo.NewHTTPError(401, "无效令牌")
            }
            
            c.Set("token", token)
            return next(c)
        }
    }
}

标准 HTTP

func JWTHandler(secret []byte, handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "缺少授权头", 401)
            return
        }
        
        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        
        token, err := jwt.ParseHS256(tokenString, secret)
        if err != nil || !token.Valid {
            http.Error(w, "无效令牌", 401)
            return
        }
        
        // 将令牌信息传递给处理器
        ctx := context.WithValue(r.Context(), "token", token)
        handler(w, r.WithContext(ctx))
    }
}

🛡️ 安全最佳实践

1. 密钥管理

// ❌ 不要这样做
secret := []byte("simple-secret")

// ✅ 生成强密钥
secret := jwt.GenerateHMACSecret(32)  // 256位

// ✅ 从环境变量读取
secret := []byte(os.Getenv("JWT_SECRET"))
if len(secret) == 0 {
    log.Fatal("JWT_SECRET 环境变量未设置")
}

// ✅ 密钥轮换
type KeyRotation struct {
    current    []byte
    previous   []byte
    rotateTime time.Time
}

func (kr *KeyRotation) ShouldRotate() bool {
    return time.Since(kr.rotateTime) > 30*24*time.Hour  // 30天轮换
}

2. 令牌生命周期

// ✅ 短生命周期 + 刷新令牌
accessToken, _ := jwt.NewBuilder(jwt.SigningMethodHS256, secret).
    SetExpirationFromNow(15 * time.Minute).  // 15分钟
    SetClaim("type", "access").
    Build()

refreshToken, _ := jwt.NewBuilder(jwt.SigningMethodHS256, secret).
    SetExpirationFromNow(7 * 24 * time.Hour).  // 7天
    SetClaim("type", "refresh").
    Build()

// ✅ 敏感操作短期令牌
adminToken, _ := jwt.NewBuilder(jwt.SigningMethodHS256, secret).
    SetExpirationFromNow(5 * time.Minute).  // 5分钟
    SetClaim("scope", "admin").
    Build()

3. 声明验证

func ValidateUserClaims(claims jwt.MapClaims) error {
    // 验证必需字段
    requiredFields := []string{"sub", "exp", "iat"}
    for _, field := range requiredFields {
        if _, ok := claims[field]; !ok {
            return fmt.Errorf("缺少必需字段: %s", field)
        }
    }
    
    // 验证用户权限
    if role, ok := claims["role"].(string); ok {
        validRoles := []string{"user", "admin", "moderator"}
        if !contains(validRoles, role) {
            return errors.New("无效的用户角色")
        }
    }
    
    // 验证自定义业务逻辑
    if userID, ok := claims["sub"].(string); ok {
        if !isValidUserID(userID) {
            return errors.New("无效的用户ID")
        }
    }
    
    return nil
}

4. 错误处理

func ParseTokenSafely(tokenString string, secret []byte) (*jwt.Token, error) {
    token, err := jwt.ParseHS256(tokenString, secret)
    if err != nil {
        // 记录错误但不暴露详细信息
        log.Printf("JWT 解析失败: %v", err)
        
        // 返回通用错误信息
        return nil, errors.New("令牌无效")
    }
    
    if !token.Valid {
        return nil, errors.New("令牌无效")
    }
    
    return token, nil
}

📊 性能优化

基准测试结果

HMAC 签名/验证:         ✅ 10万次/秒
RSA 签名/验证:          ✅ 1万次/秒
大载荷处理 (10KB):      ✅ 毫秒级完成
批量验证:              ✅ 并发安全

性能优化技巧

// 1. 预编译密钥
type FastJWT struct {
    method     jwt.SigningMethod
    key        interface{}
    keyBytes   []byte  // 预处理的密钥
}

func NewFastJWT(method jwt.SigningMethod, key interface{}) *FastJWT {
    fj := &FastJWT{method: method, key: key}
    
    // 预处理 HMAC 密钥
    if hmacMethod, ok := method.(*jwt.SigningMethodHMAC); ok {
        if keyBytes, ok := key.([]byte); ok {
            fj.keyBytes = keyBytes
        }
    }
    
    return fj
}

// 2. 复用解析器
var tokenParser = &jwt.Parser{
    ValidMethods: []string{"HS256", "HS384", "HS512"},
}

// 3. 对象池
var claimsPool = sync.Pool{
    New: func() interface{} {
        return make(jwt.MapClaims)
    },
}

func ParseWithPool(tokenString string, secret []byte) (*jwt.Token, error) {
    claims := claimsPool.Get().(jwt.MapClaims)
    defer claimsPool.Put(claims)
    
    // 清空复用的 claims
    for k := range claims {
        delete(claims, k)
    }
    
    return jwt.ParseWithClaims(jwt.SigningMethodHS256, tokenString, secret, claims)
}

🔍 故障排除

常见错误

1. 签名验证失败

// 错误原因:密钥不匹配
tokenA, _ := jwt.GenerateHS256([]byte("secret1"), claims)
_, err := jwt.ParseHS256(tokenA, []byte("secret2"))  // 失败

// 解决方案:确保密钥一致
secret := []byte("same-secret")
token, _ := jwt.GenerateHS256(secret, claims)
parsed, _ := jwt.ParseHS256(token, secret)  // 成功

2. 时间相关错误

// 错误:令牌还未生效
claims := jwt.MapClaims{
    "nbf": time.Now().Add(time.Hour).Unix(),  // 1小时后才生效
}

// 解决方案:检查服务器时间同步
// 添加时间容差
claims["nbf"] = time.Now().Add(-time.Minute).Unix()  // 允许1分钟时差

3. 算法不匹配

// 错误:生成和解析使用不同算法
token, _ := jwt.GenerateHS256(secret, claims)
_, err := jwt.ParseHS384(token, secret)  // 失败

// 解决方案:使用通用 API
method := jwt.SigningMethodHS256
token, _ := jwt.Generate(method, secret, claims)
parsed, _ := jwt.Parse(method, token, secret)  // 成功

调试技巧

// 1. 启用详细日志
func DebugToken(tokenString string) {
    parts := strings.Split(tokenString, ".")
    if len(parts) != 3 {
        fmt.Println("令牌格式错误")
        return
    }
    
    // 解码头部
    header, _ := jwt.DecodeHeader(tokenString)
    fmt.Printf("头部: %+v\n", header)
    
    // 解码载荷
    var claims jwt.MapClaims
    jwt.DecodeClaims(tokenString, &claims)
    fmt.Printf("载荷: %+v\n", claims)
    
    // 检查过期时间
    if exp, ok := claims["exp"].(float64); ok {
        expTime := time.Unix(int64(exp), 0)
        fmt.Printf("过期时间: %v (剩余: %v)\n", expTime, time.Until(expTime))
    }
}

// 2. 验证步骤分解
func StepByStepValidation(tokenString string, secret []byte) {
    fmt.Println("=== JWT 验证步骤 ===")
    
    // 步骤1:格式检查
    parts := strings.Split(tokenString, ".")
    if len(parts) != 3 {
        fmt.Println("❌ 格式错误:不是有效的JWT格式")
        return
    }
    fmt.Println("✅ 格式正确")
    
    // 步骤2:头部解析
    header, err := jwt.DecodeHeader(tokenString)
    if err != nil {
        fmt.Printf("❌ 头部解析失败: %v\n", err)
        return
    }
    fmt.Printf("✅ 头部解析成功: %+v\n", header)
    
    // 步骤3:载荷解析
    var claims jwt.MapClaims
    err = jwt.DecodeClaims(tokenString, &claims)
    if err != nil {
        fmt.Printf("❌ 载荷解析失败: %v\n", err)
        return
    }
    fmt.Printf("✅ 载荷解析成功: %+v\n", claims)
    
    // 步骤4:签名验证
    token, err := jwt.ParseHS256(tokenString, secret)
    if err != nil {
        fmt.Printf("❌ 签名验证失败: %v\n", err)
        return
    }
    fmt.Println("✅ 签名验证成功")
    
    // 步骤5:时间验证
    if !token.Valid {
        fmt.Println("❌ 令牌无效(可能已过期)")
        return
    }
    fmt.Println("✅ 令牌有效")
}

🔗 相关资源

💬 获取帮助

⚠️ **GitHub.com Fallback** ⚠️