golang实现JWT生成验证与签名的全功能插件库jwt-go的使用

golang实现JWT生成验证与签名的全功能插件库jwt-go的使用

什么是JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。它由三部分组成,用点(.)分隔:

  1. 头部(Header) - 包含令牌类型和签名算法
  2. 载荷(Payload) - 包含声明(claims),即要传输的数据
  3. 签名(Signature) - 用于验证消息未被篡改

jwt-go库简介

jwt-go是Go语言实现的JWT库,支持JWT的解析、验证、生成和签名功能。当前支持的签名算法包括:

  • HMAC SHA
  • RSA
  • RSA-PSS
  • ECDSA

安装

go get -u github.com/golang-jwt/jwt/v5

使用示例

1. 生成JWT令牌

package main

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

// 自定义Claims结构体,可以添加自定义字段
type CustomClaims struct {
	UserID int64 `json:"user_id"`
	jwt.RegisteredClaims
}

func main() {
	// 定义HMAC密钥
	hmacSecret := []byte("your-secret-key")
	
	// 创建Claims
	claims := CustomClaims{
		UserID: 12345,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    "test",
			Subject:   "somebody",
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 过期时间24小时
			IssuedAt:  jwt.NewNumericDate(time.Now()),                     // 签发时间
		},
	}
	
	// 使用HS256算法创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	
	// 签名token
	tokenString, err := token.SignedString(hmacSecret)
	if err != nil {
		fmt.Println("Error generating token:", err)
		return
	}
	
	fmt.Println("Generated token:", tokenString)
}

2. 验证JWT令牌

package main

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

type CustomClaims struct {
	UserID int64 `json:"user_id"`
	jwt.RegisteredClaims
}

func main() {
	// 假设这是从客户端接收到的token
	tokenString := "your.jwt.token.string"
	
	// 定义HMAC密钥(与生成时相同)
	hmacSecret := []byte("your-secret-key")
	
	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名算法是否正确
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return hmacSecret, nil
	})
	
	if err != nil {
		fmt.Println("Error parsing token:", err)
		return
	}
	
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		fmt.Printf("UserID: %v, ExpiresAt: %v\n", claims.UserID, claims.ExpiresAt)
	} else {
		fmt.Println("Invalid token")
	}
}

3. 使用RSA签名

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"log"
	"time"
	
	"github.com/golang-jwt/jwt/v5"
)

func main() {
	// 生成RSA私钥
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Fatalf("Error generating RSA key: %v", err)
	}
	
	// 创建Claims
	claims := jwt.RegisteredClaims{
		Issuer:    "auth-service",
		Subject:   "user123",
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
	}
	
	// 创建token
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	
	// 使用私钥签名
	tokenString, err := token.SignedString(privateKey)
	if err != nil {
		log.Fatalf("Error signing token: %v", err)
	}
	
	fmt.Println("RSA signed token:", tokenString)
	
	// 验证token
	parsedToken, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, 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 &privateKey.PublicKey, nil
	})
	
	if err != nil {
		log.Fatalf("Error parsing token: %v", err)
	}
	
	if claims, ok := parsedToken.Claims.(*jwt.RegisteredClaims); ok && parsedToken.Valid {
		fmt.Printf("Valid token for subject: %s\n", claims.Subject)
	} else {
		fmt.Println("Invalid token")
	}
}

安全注意事项

  1. 务必验证令牌的签名算法(alg)是否符合预期
  2. 使用足够强度的密钥
  3. 设置合理的令牌过期时间
  4. 不要在不安全的通道传输JWT
  5. 敏感信息不应放在JWT载荷中

jwt-go库提供了强大的JWT功能实现,可以满足大多数应用场景的需求。通过合理配置和正确使用,可以构建安全的认证和授权系统。


更多关于golang实现JWT生成验证与签名的全功能插件库jwt-go的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现JWT生成验证与签名的全功能插件库jwt-go的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang JWT 生成、验证与签名全功能指南

JWT (JSON Web Token) 是现代Web应用中常用的身份验证机制。在Go语言中,jwt-go是最流行的JWT处理库。下面我将详细介绍如何使用jwt-go实现JWT的生成、验证与签名功能。

1. 安装jwt-go

首先安装jwt-go库:

go get github.com/dgrijalva/jwt-go

2. 基本概念

JWT由三部分组成:

  • Header:包含令牌类型和签名算法
  • Payload:包含声明(claims)
  • Signature:用于验证令牌完整性的签名

3. 生成JWT令牌

3.1 使用HMAC SHA256签名

package main

import (
	"fmt"
	"time"
	
	"github.com/dgrijalva/jwt-go"
)

// 定义自定义claims结构体
type CustomClaims struct {
	UserID int64 `json:"user_id"`
	Username string `json:"username"`
	jwt.StandardClaims
}

func main() {
	// 定义密钥
	mySigningKey := []byte("AllYourBase")

	// 创建claims
	claims := CustomClaims{
		UserID:   12345,
		Username: "john_doe",
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 过期时间24小时
			Issuer:    "test",                                // 签发者
		},
	}

	// 创建token对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 生成签名字符串
	tokenString, err := token.SignedString(mySigningKey)
	if err != nil {
		fmt.Println("Error generating token:", err)
		return
	}

	fmt.Println("Generated token:", tokenString)
}

3.2 使用RSA签名

func generateTokenWithRSA() {
	// 读取私钥
	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(`-----BEGIN RSA PRIVATE KEY-----
...你的私钥内容...
-----END RSA PRIVATE KEY-----`))
	if err != nil {
		fmt.Println("Error parsing private key:", err)
		return
	}

	// 创建claims
	claims := jwt.MapClaims{
		"user_id": 12345,
		"exp":     time.Now().Add(time.Hour * 24).Unix(),
	}

	// 创建并签名token
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	tokenString, err := token.SignedString(privateKey)
	if err != nil {
		fmt.Println("Error signing token:", err)
		return
	}

	fmt.Println("RSA signed token:", tokenString)
}

4. 验证JWT令牌

4.1 验证HMAC签名的令牌

func validateToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名算法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return []byte("AllYourBase"), nil
	})

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	} else {
		return nil, err
	}
}

4.2 验证RSA签名的令牌

func validateRSAToken(tokenString string) (jwt.MapClaims, error) {
	// 读取公钥
	publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(`-----BEGIN PUBLIC KEY-----
...你的公钥内容...
-----END PUBLIC KEY-----`))
	if err != nil {
		return nil, fmt.Errorf("error parsing public key: %v", err)
	}

	token, err := 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
	})

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, nil
	} else {
		return nil, err
	}
}

5. 高级用法

5.1 刷新令牌

func refreshToken(oldToken string) (string, error) {
	// 先验证旧令牌
	claims, err := validateToken(oldToken)
	if err != nil {
		return "", err
	}

	// 检查令牌是否即将过期(比如剩余有效期小于15分钟)
	if time.Unix(claims.ExpiresAt, 0).Sub(time.Now()) > time.Minute*15 {
		return "", fmt.Errorf("token is not expired yet")
	}

	// 创建新claims,延长有效期
	newClaims := CustomClaims{
		UserID:   claims.UserID,
		Username: claims.Username,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
			Issuer:    claims.Issuer,
		},
	}

	// 生成新令牌
	newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
	return newToken.SignedString([]byte("AllYourBase"))
}

5.2 黑名单处理

var tokenBlacklist = make(map[string]bool)

func invalidateToken(tokenString string) error {
	// 验证令牌
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte("AllYourBase"), nil
	})
	if err != nil {
		return err
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		// 将令牌ID加入黑名单
		tokenBlacklist[claims.Id] = true
		return nil
	}
	return fmt.Errorf("invalid token")
}

func isTokenInvalid(tokenString string) bool {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte("AllYourBase"), nil
	})
	if err != nil {
		return true
	}

	if claims, ok := token.Claims.(*CustomClaims); ok {
		return tokenBlacklist[claims.Id]
	}
	return true
}

6. 最佳实践

  1. 密钥管理:不要将密钥硬编码在代码中,使用环境变量或密钥管理系统
  2. 令牌有效期:设置合理的过期时间,通常访问令牌1-2小时,刷新令牌7-30天
  3. HTTPS:始终通过HTTPS传输JWT
  4. 敏感数据:不要在JWT中存储敏感信息,因为JWT可以被解码
  5. 签名算法:推荐使用RS256而非HS256,因为前者可以分离验证和签名密钥

7. 总结

通过jwt-go库,我们可以轻松实现JWT的生成、验证和签名功能。本文介绍了HMAC和RSA两种签名方式,以及令牌刷新、黑名单等高级功能。在实际应用中,请根据安全需求选择合适的签名算法,并遵循JWT的最佳实践。

对于生产环境,建议考虑使用更现代的替代库如github.com/golang-jwt/jwt,因为dgrijalva/jwt-go已不再维护。

回到顶部