golang安全简单高效的JSON Web Tokens生成与验证插件库jwt的使用

Golang安全简单高效的JSON Web Tokens生成与验证插件库jwt的使用

简介

这是一个用于Go语言的JSON Web Token(JWT)实现库,遵循RFC 7519标准。该库设计简洁、API清晰、性能优化且线程安全。

特性

  • 简单易用的API
  • 干净且经过测试的代码
  • 针对速度进行了优化
  • 线程安全
  • 无依赖
  • 支持所有常见算法:
    • HMAC (HS)
    • RSA (RS)
    • RSA-PSS (PS)
    • ECDSA (ES)
    • EdDSA (EdDSA)
    • 或自定义算法

安装

Go版本要求1.17+

go get github.com/cristalhq/jwt/v5

使用示例

生成JWT Token

// 创建一个签名器(这里使用HMAC示例)
key := []byte(`secret`)
signer, err := jwt.NewSignerHS(jwt.HS256, key)
if err != nil {
    panic(err)
}

// 创建声明(可以创建自定义声明)
claims := &jwt.RegisteredClaims{
    Audience: []string{"admin"},
    ID:       "random-unique-string",
}

// 创建Builder
builder := jwt.NewBuilder(signer)

// 构建Token
token, err := builder.Build(claims)
if err != nil {
    panic(err)
}

// 获取Token字符串
tokenString := token.String()
fmt.Println("Generated token:", tokenString)

解析和验证JWT Token

// 创建一个验证器(这里使用HMAC示例)
key := []byte(`secret`)
verifier, err := jwt.NewVerifierHS(jwt.HS256, key)
if err != nil {
    panic(err)
}

// 解析并验证Token
tokenBytes := []byte(tokenString)
newToken, err := jwt.Parse(tokenBytes, verifier)
if err != nil {
    panic(err)
}

// 或者只验证签名
err = verifier.Verify(newToken)
if err != nil {
    panic(err)
}

// 获取注册声明
var newClaims jwt.RegisteredClaims
err = json.Unmarshal(newToken.Claims(), &newClaims)
if err != nil {
    panic(err)
}

// 或者直接解析声明
err = jwt.ParseClaims(tokenBytes, verifier, &newClaims)
if err != nil {
    panic(err)
}

// 验证声明
isForAdmin := newClaims.IsForAudience("admin")
isValid := newClaims.IsValidAt(time.Now())
fmt.Printf("Is for admin: %v, Is valid: %v\n", isForAdmin, isValid)

自定义声明

// 自定义声明结构
type UserClaims struct {
    jwt.RegisteredClaims
    Username string `json:"username"`
    IsAdmin  bool   `json:"isAdmin"`
}

// 使用自定义声明
userClaims := &UserClaims{
    RegisteredClaims: jwt.RegisteredClaims{
        ID: "user-123",
    },
    Username: "john_doe",
    IsAdmin:  true,
}

// 构建Token
token, err := builder.Build(userClaims)
if err != nil {
    panic(err)
}

// 解析时使用自定义声明
var parsedClaims UserClaims
err = jwt.ParseClaims(token.Bytes(), verifier, &parsedClaims)
if err != nil {
    panic(err)
}

fmt.Printf("User: %s, Admin: %v\n", parsedClaims.Username, parsedClaims.IsAdmin)

注意事项

  1. 确保使用足够强度的密钥
  2. 合理设置Token的过期时间
  3. 不要在不安全的通道传输Token
  4. 验证Token时检查所有必要的声明

这个库提供了简单而强大的JWT功能,适合大多数Go应用程序的安全需求。


更多关于golang安全简单高效的JSON Web Tokens生成与验证插件库jwt的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang安全简单高效的JSON Web Tokens生成与验证插件库jwt的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang JWT 库使用指南

JSON Web Tokens (JWT) 是一种流行的身份验证和授权机制,在Go语言中有多个优秀的JWT实现库。下面我将介绍最常用的 github.com/golang-jwt/jwt 库的使用方法。

安装

首先安装库:

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

基本使用

1. 生成JWT Token

package main

import (
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

// 自定义Claims结构体
type MyCustomClaims struct {
	UserID   int    `json:"user_id"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

func main() {
	// 定义秘钥
	mySigningKey := []byte("your-256-bit-secret")

	// 创建Claims
	claims := MyCustomClaims{
		12345,
		"john_doe",
		jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 过期时间24小时
			IssuedAt:  jwt.NewNumericDate(time.Now()),                     // 签发时间
			NotBefore: jwt.NewNumericDate(time.Now()),                     // 生效时间
			Issuer:    "test",                                             // 签发者
			Subject:   "somebody",                                         // 主题
			ID:        "1",                                                // 唯一ID
			Audience:  []string{"somebody_else"},                         // 受众
		},
	}

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

	// 签名并获取完整Token
	ss, err := token.SignedString(mySigningKey)
	if err != nil {
		fmt.Printf("Error creating token: %v\n", err)
		return
	}

	fmt.Printf("Token: %v\n", ss)
}

2. 验证JWT Token

func verifyToken(tokenString string) (*MyCustomClaims, error) {
	// 解析Token
	token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, 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("your-256-bit-secret"), nil
	})

	if err != nil {
		return nil, err
	}

	// 验证Claims
	if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, fmt.Errorf("invalid token")
}

高级用法

1. 使用RSA非对称加密

// 生成RSA Token
func generateRSAToken() (string, error) {
	// 读取私钥
	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(`-----BEGIN RSA PRIVATE KEY-----
	... 私钥内容 ...
	-----END RSA PRIVATE KEY-----`))
	if err != nil {
		return "", err
	}

	claims := jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
		Issuer:    "auth-service",
	}

	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	return token.SignedString(privateKey)
}

// 验证RSA Token
func verifyRSAToken(tokenString string) (*jwt.RegisteredClaims, error) {
	// 读取公钥
	publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(`-----BEGIN PUBLIC KEY-----
	... 公钥内容 ...
	-----END PUBLIC KEY-----`))
	if err != nil {
		return nil, err
	}

	token, 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 publicKey, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, fmt.Errorf("invalid token")
}

2. 刷新Token

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

	// 检查是否在可刷新时间范围内
	if time.Until(claims.ExpiresAt.Time) > 30*time.Minute {
		return "", fmt.Errorf("token is not expired yet")
	}

	// 创建新Token
	newClaims := MyCustomClaims{
		UserID:   claims.UserID,
		Username: claims.Username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    claims.Issuer,
		},
	}

	newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
	return newToken.SignedString([]byte("your-256-bit-secret"))
}

安全建议

  1. 秘钥管理:不要将秘钥硬编码在代码中,使用环境变量或秘钥管理服务
  2. 过期时间:设置合理的过期时间,通常访问令牌1小时,刷新令牌7-30天
  3. 算法选择:优先使用RS256而非HS256,避免对称加密的安全风险
  4. 敏感数据:不要在JWT中存储敏感信息,JWT可以被解码查看
  5. HTTPS:始终通过HTTPS传输JWT

错误处理

func handleTokenError(err error) {
	if ve, ok := err.(*jwt.ValidationError); ok {
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			fmt.Println("That's not even a token")
		} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
			fmt.Println("Token is expired")
		} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
			fmt.Println("Token is not active yet")
		} else {
			fmt.Println("Couldn't handle this token:", err)
		}
	} else {
		fmt.Println("Couldn't parse token:", err)
	}
}

以上就是Go语言中使用JWT的基本方法,这个库简单易用且功能强大,适合大多数应用场景。

回到顶部