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 由三部分组成,用点号分隔:
Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- Header - 算法和令牌类型
- Payload - 声明信息 (Claims)
- Signature - 签名,确保数据完整性
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 让算法切换更容易:
// 支持的算法
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 算法使用共享密钥,适合单体应用或内部服务:
// 生成强密钥
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 密钥对
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), // 过期时间
)
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,
})
})
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)
}
}
}
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))
}
}
// ❌ 不要这样做
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天轮换
}
// ✅ 短生命周期 + 刷新令牌
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()
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
}
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)
}
// 错误原因:密钥不匹配
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) // 成功
// 错误:令牌还未生效
claims := jwt.MapClaims{
"nbf": time.Now().Add(time.Hour).Unix(), // 1小时后才生效
}
// 解决方案:检查服务器时间同步
// 添加时间容差
claims["nbf"] = time.Now().Add(-time.Minute).Unix() // 允许1分钟时差
// 错误:生成和解析使用不同算法
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("✅ 令牌有效")
}
- 📚 JWT 官方规范 (RFC 7519)
- 🔍 JWT.io 调试工具
- 📖 JWT API 文档
- 🏠 返回 Wiki 首页
- 🚀 快速开始指南
- 🐛 报告问题
- 💡 功能建议
- 📧 技术支持:[email protected]