JWT 认证中间件设计

JWT 认证的完整设计:access token + refresh token 双 token 策略、签名验证、中间件注入上下文、安全注意事项。

#type / concept #status / growing #tech / dev / backend #resource / go

[!info] related notes

JWT 认证中间件设计

双 Token 策略

TokenTTL存储用途
Access Token短(7天)前端内存/localStorageAPI 请求认证
Refresh Token长(30天)数据库刷新 access token

Access token 泄露的影响窗口是 TTL 时间。Refresh token 可以通过数据库黑名单立即失效。

Claims 设计

type Claims struct {
    UserID uuid.UUID `json:"user_id"`
    Email  string    `json:"email"`
    jwt.RegisteredClaims
}
  • UserID: 业务标识,handler 通过 c.Get("user_id") 获取
  • RegisteredClaims: 标准字段(exp, iat, sub),jwt 库自动验证

签名验证

func Validate(cfg JWTConfig, tokenString string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &Claims{},
        func(token *jwt.Token) (interface{}, error) {
            // 验证签名方法,防止 alg 攻击
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
            }
            return []byte(cfg.SecretKey), nil
        },
    )
    // ...
}

关键安全检查:验证签名方法是 HMAC,防止攻击者把 alg 改成 “none” 绕过签名。

中间件

func AuthMiddleware(jwtConfig auth.JWTConfig) gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if !strings.HasPrefix(authHeader, "Bearer ") {
            c.JSON(401, gin.H{"error": "unauthorized"})
            c.Abort()
            return
        }

        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        claims, err := auth.Validate(jwtConfig, tokenString)
        if err != nil {
            c.JSON(401, gin.H{"error": "invalid token"})
            c.Abort()
            return
        }

        c.Set("user_id", claims.UserID.String())
        c.Set("email", claims.Email)
        c.Next()
    }
}

安全注意事项

  1. Secret Key 必须随机 — 生产环境用 openssl rand -hex 32
  2. 不要在 JWT 中存敏感信息 — JWT 可以被 base64 解码
  3. HTTPS 必须 — JWT 在传输中被截获可以重放
  4. Access Token 短期 — 泄露影响窗口小
  5. Refresh Token 可以注销 — 存数据库,服务端验证有效性
创建于 2026/6/25 更新于 2026/6/25