我来为你详细介绍在 Go 语言中使用 JWT 的实践方案。

Go 语言 JWT 库推荐

Go 生态中最流行的 JWT 库是 `github.com/golang-jwt/jwt/v5`(原 `dgrijalva/jwt-go` 的社区维护分支,更安全可靠)。

快速开始

1. 安装依赖

```bash
go get github.com/golang-jwt/jwt/v5
```

2. 基础使用示例

生成 Token(登录时)

```go
package main

import (
    "time"
    "github.com/golang-jwt/jwt/v5"
)

// 定义密钥(生产环境应从环境变量读取)
var jwtSecret = []byte("your-256-bit-secret")

// 自定义 Claims 结构
type UserClaims struct {
    UserID   uint   `json:"user_id"`
    Username string `json:"username"`
    jwt.RegisteredClaims  // 内嵌标准声明
}

// 生成 JWT
func GenerateToken(userID uint, username string) (string, error) {
    claims := UserClaims{
        UserID:   userID,
        Username: username,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 24小时过期
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            Subject:   "user_token",
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}
```

解析和验证 Token(中间件中)

```go
// 解析 JWT
func ParseToken(tokenString string) (*UserClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
        // 验证签名算法
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, jwt.ErrSignatureInvalid
        }
        return jwtSecret, nil
    })
    
    if err != nil {
        return nil, err
    }
    
    if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
        return claims, nil
    }
    return nil, jwt.ErrInvalidKey
}
```

Gin 框架集成示例

这是实际项目中最常见的使用场景:

```go
package main

import (
    "net/http"
    "strings"
    "time"
    
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v5"
)

var jwtSecret = []byte("your-secret-key")

// JWT 中间件
func JWTAuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "缺少认证头"})
            c.Abort()
            return
        }
        
        // 提取 Bearer token
        parts := strings.SplitN(authHeader, " ", 2)
        if !(len(parts) == 2 && parts[0] == "Bearer") {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "认证头格式错误"})
            c.Abort()
            return
        }
        
        // 解析 token
        claims, err := ParseToken(parts[1])
        if err != nil {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的token"})
            c.Abort()
            return
        }
        
        // 将用户信息存入上下文
        c.Set("userID", claims.UserID)
        c.Set("username", claims.Username)
        c.Next()
    }
}

func main() {
    r := gin.Default()
    
    // 公开路由
    r.POST("/login", loginHandler)
    
    // 需要认证的路由组
    api := r.Group("/api")
    api.Use(JWTAuthMiddleware())
    {
        api.GET("/profile", profileHandler)
        api.POST("/logout", logoutHandler)
    }
    
    r.Run(":8080")
}

// 登录处理
func loginHandler(c *gin.Context) {
    var req struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }
    
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // 验证用户名密码(示例,实际应查数据库)
    if req.Username != "admin" || req.Password != "123456" {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "认证失败"})
        return
    }
    
    // 生成 token
    token, err := GenerateToken(1, req.Username)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "生成token失败"})
        return
    }
    
    c.JSON(http.StatusOK, gin.H{
        "token": token,
        "type":  "Bearer",
    })
}

// 获取个人信息(需要认证)
func profileHandler(c *gin.Context) {
    userID, _ := c.Get("userID")
    username, _ := c.Get("username")
    
    c.JSON(http.StatusOK, gin.H{
        "user_id":  userID,
        "username": username,
    })
}
```

高级特性

使用 RSA 非对称加密(更安全的微服务场景)

```go
// 生成 Token(使用私钥)
func GenerateTokenWithRSA(userID uint, privateKey *rsa.PrivateKey) (string, error) {
    claims := jwt.MapClaims{
        "user_id": userID,
        "exp":     time.Now().Add(time.Hour * 24).Unix(),
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
    return token.SignedString(privateKey)
}

// 验证 Token(使用公钥)
func ParseTokenWithRSA(tokenString string, publicKey *rsa.PublicKey) (*jwt.Token, error) {
    return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return publicKey, nil
    })
}
```

Token 刷新机制

```go
// RefreshToken 在 token 即将过期时生成新 token
func RefreshToken(tokenString string) (string, error) {
    claims, err := ParseToken(tokenString)
    if err != nil {
        return "", err
    }
    
    // 检查是否需要刷新(例如剩余时间小于30分钟)
    if time.Until(claims.ExpiresAt.Time) > 30*time.Minute {
        return "", errors.New("token 尚未需要刷新")
    }
    
    // 生成新 token
    return GenerateToken(claims.UserID, claims.Username)
}
```

目录结构建议

```
project/
├── internal/
│   ├── middleware/
│   │   └── jwt.go          # JWT 中间件
│   └── utils/
│       └── jwt.go          # JWT 工具函数
├── pkg/
│   └── config/
│       └── config.go       # 密钥配置
└── cmd/
    └── server/
        └── main.go
```

安全最佳实践

实践    说明    
密钥管理    使用环境变量或密钥管理服务(KMS),不要硬编码    
算法选择    优先使用 `HS256` 或 `RS256`,避免 `none` 算法    
过期时间    设置合理的过期时间(建议 15分钟-2小时)    
HTTPS 传输    生产环境必须使用 HTTPS    
Token 存储    前端存储在 `httpOnly` Cookie 或内存中,避免 LocalStorage XSS 风险    

需要了解更具体的场景实现(如 Redis 存储黑名单实现登出、多设备登录控制等)吗?

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐