不灭的焱

加密类型:SHA/AES/RSA下载Go
复合类型:切片(slice)、映射(map)、指针(pointer)、函数(function)、通道(channel)、接口(interface)、数组(array)、结构体(struct) Go类型+零值nil
引用类型:切片(slice)、映射(map)、指针(pointer)、函数(function)、通道(channel) Go引用

作者:AlbertWen  添加时间:2025-12-29 14:57:54  修改时间:2025-12-29 20:53:49  分类:02.Go语言编程  编辑

相关文章:

  1. Java使用JWT实现Token认证
  2. 【Spring Boot】JWT-Token验证

想要学习 golang-jwt(通常指 github.com/golang-jwt/jwt/v5,是原 github.com/dgrijalva/jwt-go 的官方维护续版)的使用,本教程将从环境准备、JWT 生成、验证、核心特性等方面完整讲解,确保你能快速上手。

一、环境准备

1. 安装 golang-jwt 包

首先在你的 Go 项目中安装 jwt/v5 版本(推荐使用 v5 稳定版,兼容 Go 1.16+ 模块机制):

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

2. 核心概念说明

JWT 由三部分组成:Header(头部)、Payload(载荷)、Signature(签名),golang-jwt 核心完成签名生成签名验证两大核心功能,其中:

  • 密钥(Secret/Key)是关键:对称加密使用单一密钥,非对称加密使用公钥 / 私钥对
  • 载荷包含标准声明(如过期时间、发行人)和自定义声明(业务数据)

二、对称加密(HS256/HS512)使用(最常用)

对称加密使用同一把密钥进行签名和验证,操作简单、性能高,适合服务内部的 JWT 生成与验证。

步骤 1:定义核心常量

package main

import (
	"errors"
	"fmt"
	"time"

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

// 1. 定义对称加密密钥(生产环境请存储在环境变量/配置中心,不要硬编码)
const secretKey = "your-strong-secret-key-32bytes-long-recommend" // 推荐使用 32/64 字节强密钥

// 2. 定义自定义载荷结构体(嵌入 jwt.RegisteredClaims 以支持标准声明)
type CustomClaims struct {
	// 自定义业务字段(根据需求添加)
	UserID   uint64 `json:"user_id"`
	Username string `json:"username"`
	// 嵌入标准声明(必须,v5 版本强制要求使用 RegisteredClaims 管理标准字段)
	jwt.RegisteredClaims
}

步骤 2:生成 JWT Token

核心流程:

  1. 构造自定义载荷(包含标准声明和业务字段)
  2. 创建 JWT 令牌对象,指定签名算法(HS256)
  3. 使用密钥进行签名,生成字符串格式的 Token
// GenerateToken 生成对称加密的 JWT Token
func GenerateToken(userID uint64, username string) (string, error) {
	// 1. 设置令牌过期时间(推荐设置,避免 Token 永久有效)
	expireTime := time.Now().Add(2 * time.Hour) // 2 小时过期

	// 2. 构造自定义载荷
	claims := CustomClaims{
		UserID:   userID,
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			// 标准声明字段(按需设置)
			ExpiresAt: jwt.NewNumericDate(expireTime), // 过期时间(必填,核心验证字段)
			IssuedAt:  jwt.NewNumericDate(time.Now()),  // 签发时间
			NotBefore: jwt.NewNumericDate(time.Now()),  // 生效时间(立即生效)
			Issuer:    "your-app-name",                 // 签发人(应用标识)
			Subject:   fmt.Sprintf("%d", userID),       // 主题(通常为用户唯一标识)
		},
	}

	// 3. 创建 JWT 令牌对象,指定签名算法和载荷
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 4. 使用密钥签名,生成 Token 字符串
	tokenString, err := token.SignedString([]byte(secretKey))
	if err != nil {
		return "", fmt.Errorf("生成 Token 失败:%w", err)
	}

	return tokenString, nil
}

步骤 3:验证 JWT Token 并解析载荷

核心流程:

  1. 解析 Token 字符串,指定验证密钥和声明类型
  2. 验证 Token 的签名有效性、过期时间等
  3. 提取验证通过后的自定义载荷数据
// ParseToken 验证并解析 JWT Token
func ParseToken(tokenString string) (*CustomClaims, error) {
	// 1. 解析 Token(第二个参数为密钥获取函数,返回验证用的密钥)
	token, err := jwt.ParseWithClaims(
		tokenString,
		&CustomClaims{}, // 指定要解析到的自定义声明类型
		func(token *jwt.Token) (interface{}, error) {
			// 验证签名算法是否与预期一致(防止算法篡改攻击)
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("不支持的签名算法:%v", token.Header["alg"])
			}
			// 返回对称加密密钥
			return []byte(secretKey), nil
		},
	)
	if err != nil {
		return nil, fmt.Errorf("解析 Token 失败:%w", err)
	}

	// 2. 验证 Token 是否有效(签名有效、未过期等)
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		// 3. 返回解析后的自定义载荷
		return claims, nil
	}

	return nil, errors.New("无效的 Token")
}

步骤 4:测试使用

func main() {
	// 1. 生成 Token
	token, err := GenerateToken(1001, "zhangsan")
	if err != nil {
		fmt.Printf("生成 Token 出错:%v\n", err)
		return
	}
	fmt.Printf("生成的 JWT Token:\n%s\n\n", token)

	// 2. 验证并解析 Token
	claims, err := ParseToken(token)
	if err != nil {
		fmt.Printf("解析 Token 出错:%v\n", err)
		return
	}

	// 3. 打印解析后的载荷数据
	fmt.Printf("解析成功,用户 ID:%d\n", claims.UserID)
	fmt.Printf("解析成功,用户名:%s\n", claims.Username)
	fmt.Printf("解析成功,过期时间:%v\n", claims.ExpiresAt.Time)
	fmt.Printf("解析成功,签发人:%s\n", claims.Issuer)
}

三、非对称加密(RS256)使用(适合跨服务场景)

非对称加密使用「私钥签名、公钥验证」,适合多服务协作场景(如授权中心生成 Token,其他服务仅用公钥验证,无需暴露私钥)。

步骤 1:生成 RSA 密钥对

首先通过命令行生成 RSA 私钥和公钥:

# 生成 RSA 私钥(2048 位,保存为 private.pem)
openssl genrsa -out private.pem 2048

# 从私钥提取公钥(保存为 public.pem)
openssl rsa -in private.pem -pubout -out public.pem

步骤 2:私钥生成 Token

// GenerateRSAToken 使用 RSA 私钥生成 Token
func GenerateRSAToken(userID uint64, username string) (string, error) {
	// 1. 读取 RSA 私钥文件
	privateKeyBytes, err := os.ReadFile("private.pem")
	if err != nil {
		return "", fmt.Errorf("读取私钥失败:%w", err)
	}

	// 2. 解析 RSA 私钥
	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyBytes)
	if err != nil {
		return "", fmt.Errorf("解析私钥失败:%w", err)
	}

	// 3. 构造载荷(与对称加密一致)
	expireTime := time.Now().Add(2 * time.Hour)
	claims := CustomClaims{
		UserID:   userID,
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expireTime),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "your-app-name",
		},
	}

	// 4. 指定 RS256 算法,创建 Token 并签名
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	tokenString, err := token.SignedString(privateKey)
	if err != nil {
		return "", fmt.Errorf("RSA 签名 Token 失败:%w", err)
	}

	return tokenString, nil
}

步骤 3:公钥验证 Token

// ParseRSAToken 使用 RSA 公钥验证并解析 Token
func ParseRSAToken(tokenString string) (*CustomClaims, error) {
	// 1. 读取 RSA 公钥文件
	publicKeyBytes, err := os.ReadFile("public.pem")
	if err != nil {
		return "", fmt.Errorf("读取公钥失败:%w", err)
	}

	// 2. 解析 RSA 公钥
	publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyBytes)
	if err != nil {
		return "", fmt.Errorf("解析公钥失败:%w", err)
	}

	// 3. 解析并验证 Token
	token, err := jwt.ParseWithClaims(
		tokenString,
		&CustomClaims{},
		func(token *jwt.Token) (interface{}, error) {
			// 验证签名算法为 RS256
			if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("不支持的签名算法:%v", token.Header["alg"])
			}
			return publicKey, nil
		},
	)
	if err != nil {
		return nil, fmt.Errorf("解析 RSA Token 失败:%w", err)
	}

	// 4. 返回有效载荷
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, errors.New("无效的 RSA Token")
}

四、核心注意事项(生产环境必备)

  1. 密钥安全
  • 对称加密密钥不要硬编码,应存储在环境变量(如 os.Getenv("JWT_SECRET"))、配置中心(如 Nacos、Consul)或加密存储
  • RSA 私钥严格保密,避免泄露,公钥可公开分发
  1. 设置合理过期时间
  • 避免 Token 永久有效,推荐设置 1-24 小时过期,短期操作可设置更短时间
  • 如需长期有效,可配合「刷新令牌(Refresh Token)」机制
  1. 验证签名算法
  • 解析 Token 时必须验证算法是否与预期一致,防止「算法篡改攻击」(如将 HS256 改为 none 绕过验证)
  1. 自定义声明不存敏感数据
  • JWT 载荷仅做 Base64 编码(可轻松解码),不要存储密码、身份证等敏感信息
  1. 错误处理
  • 生产环境避免返回详细的 JWT 错误信息给前端,防止泄露系统细节
  1. 版本选择
  • 始终使用 github.com/golang-jwt/jwt/v5,原 dgrijalva/jwt-go 已停止维护,存在安全风险

五、运行结果示例

生成的 JWT Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VybmFtZSI6InpoYW5nc2FuIiwiZXhwIjoxNzM1NjQyNjYwLCJpYXQiOjE3MzU2MzU0NjAsIm5iZiI6MTczNTYzNTQ2MCwiaXNzIjoieW91ci1hcHAtbmFtZSIsInN1YiI6IjEwMDEifQ.xxxxxxxxx(签名部分)

解析成功,用户 ID:1001
解析成功,用户名:zhangsan
解析成功,过期时间:2025-12-28 18:17:40 +0800 CST
解析成功,签发人:your-app-name

总结

  1. golang-jwt 核心使用 jwt.NewWithClaims() 生成 Token,jwt.ParseWithClaims() 验证解析 Token
  2. 对称加密(HS256)简单高效,适合单服务场景;非对称加密(RS256)更安全,适合跨服务场景
  3. 自定义载荷必须嵌入 jwt.RegisteredClaims,以支持标准过期时间等核心验证
  4. 生产环境重点关注密钥安全、过期时间设置和算法验证,避免安全漏洞