JWT 实现 HSRSESED 签名与验签

签名方式 算法 密钥类型 签名要点 验签要点
HS HMAC-SHA256 对称密钥 - 使用 crypto/hmac 和对称密钥生成 HMAC 签名
- 将 header.payload 作为数据输入
- 使用同一密钥重新计算 HMAC 签名
- 比较计算结果与接收到的签名是否一致
RS RSA-SHA256 公钥 + 私钥 - 使用 crypto/rsa 生成 RSA 签名
- 私钥签名,输入为 header.payload 的哈希值
- 使用 crypto/rsa 验证 RSA 签名
- 公钥解密签名后,验证是否与输入哈希值匹配
ES ECDSA-P256 公钥 + 私钥 - 使用 crypto/ecdsa 生成 ECDSA 签名
- 签名结果为 (r, s),序列化并编码为 Base64URL
- 使用 crypto/ecdsa 验证签名
- 解析签名为 (r, s),验证其与 header.payload 的哈希匹配
ED Ed25519 公钥 + 私钥 - 使用 crypto/ed25519 私钥直接签名完整的 header.payload 数据
- 签名结果无需额外哈希处理
- 使用 crypto/ed25519 公钥直接验证签名是否匹配完整数据

签名与验签实现重点

  • es算法签名和验签时算法位数必须相同:ES算法在验签时必须严格使用与签名时相同位数的算法进行验证,这一点与其他算法有所不同。(其他算法不必相同) 说明如下:

  • 加密在这里插入图片描述

  • 验签 在这里插入图片描述

  • Base64URL 编码:JWT 的 HeaderPayload 都需编码。

  • 数据输入:签名计算与验证的输入数据始终是 Base64URL(Header) + "." + Base64URL(Payload)

  • 密钥管理:对称密钥 (HS) 要妥善分发,公私钥对 (RS/ES/ED) 要安全存储。


go案例

hs.go

package jwtex

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

type HS struct {
	Key        string
	SignMethod HSSignMethod
}

type HSSignMethod string

const (
	HS256 HSSignMethod = "HS256"
	HS384 HSSignMethod = "HS384"
	HS512 HSSignMethod = "HS512"
)

// hs HMAC(Hash-based Message Authentication Code)用的hash-based
func (hs *HS) getSignMethod() *jwt.SigningMethodHMAC {
	// *jwt.SigningMethodHMAC 是 jwt.SigningMethod 接口的具体实现之一。通过返回具体的实现类型,
	// 可以确保你使用的是 HMAC 签名方法,而不是其他类型的签名方法(如 RSA 或 ECDSA)。
	switch hs.SignMethod {
	case HS256:
		return jwt.SigningMethodHS256
	case HS384:
		return jwt.SigningMethodHS384
	case HS512:
		return jwt.SigningMethodHS512
	default:
		return jwt.SigningMethodHS256
	}
}

// Sign 签名
func (hs *HS) Sign(data jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(hs.getSignMethod(), data)
	sign, err := token.SignedString([]byte(hs.Key))
	if err != nil {
		log.Println(err)
		return "", err
	}
	return sign, nil
}

// Verify 验签,获取数据
func (hs *HS) Verify(sign string, data jwt.Claims) error {
	_, err := jwt.ParseWithClaims(sign, data, func(token *jwt.Token) (interface{}, error) {
		return []byte(hs.Key), nil
	})
	return err
}


rsa.go 私钥签名、公钥验证

package jwtex

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

type RS struct {
	SignMethod RSSignMethod
	PublicKey  string
	PrivateKey string
}

type RSSignMethod string

const (
	RS256 RSSignMethod = "RS256"
	RS384 RSSignMethod = "RS384"
	RS512 RSSignMethod = "RS512"
)

func (rs *RS) getSignMethod() *jwt.SigningMethodRSA {
	switch rs.SignMethod {
	case RS512:
		return jwt.SigningMethodRS512
	case RS384:
		return jwt.SigningMethodRS384
	case RS256:
		return jwt.SigningMethodRS256
	default:
		return jwt.SigningMethodRS256
	}
}

// Sign 签名	私钥签名、公钥验证
func (rs *RS) Sign(data jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(rs.getSignMethod(), data)
	pKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(rs.PrivateKey))
	sign, err := token.SignedString(pKey)
	if err != nil {
		log.Println(err)
		return "", err
	}
	return sign, nil
}

// Verify 验签,获取数据
func (rs *RS) Verify(sign string, data jwt.Claims) error {
	_, err := jwt.ParseWithClaims(sign, data, func(token *jwt.Token) (interface{}, error) {
		return jwt.ParseRSAPublicKeyFromPEM([]byte(rs.PublicKey))
	})
	return err
}


es.go 私钥签名、公钥验证

package jwtex

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

type ES struct {
	SignMethod ESSignMethod
	PublicKey  string
	PrivateKey string
}

type ESSignMethod string

const (
	ES256 ESSignMethod = "ES256"
	ES384 ESSignMethod = "ES384"
	ES512 ESSignMethod = "ES512"
)

func (es *ES) getSignMethod() *jwt.SigningMethodECDSA {
	switch es.SignMethod {
	case ES512:
		return jwt.SigningMethodES512
	case ES384:
		return jwt.SigningMethodES384
	case ES256:
		return jwt.SigningMethodES256
	default:
		return jwt.SigningMethodES256
	}
}

// Sign 签名	私钥签名、公钥验证
func (es *ES) Sign(data jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(es.getSignMethod(), data)
	pKey, err := jwt.ParseECPrivateKeyFromPEM([]byte(es.PrivateKey))
	if err != nil {
		log.Println(err)
		return "", err
	}
	sign, err := token.SignedString(pKey)
	if err != nil {
		log.Println(err)
		return "", err
	}
	return sign, nil
}

// Verify 验签,获取数据
func (es *ES) Verify(sign string, data jwt.Claims) error {
	_, err := jwt.ParseWithClaims(sign, data, func(token *jwt.Token) (interface{}, error) {
		return jwt.ParseECPublicKeyFromPEM([]byte(es.PublicKey))
	})
	return err
}


ed.go 私钥签名、公钥验证

package jwtex

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

type ED struct {
	PrivateKey string
	PublicKey  string
}

// Sign 签名
func (ed *ED) Sign(data jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, data)
	pKey, err := jwt.ParseEdPrivateKeyFromPEM([]byte(ed.PrivateKey))
	if err != nil {
		log.Println(err)
		return "", err
	}
	sign, err := token.SignedString(pKey)
	if err != nil {
		log.Println(err)
		return "", err
	}
	return sign, err
}

// Verify 验签,并获取数据
func (ed *ED) Verify(sign string, data jwt.Claims) error {
	_, err := jwt.ParseWithClaims(sign, data, func(token *jwt.Token) (interface{}, error) {
		return jwt.ParseEdPublicKeyFromPEM([]byte(ed.PublicKey))
	})
	return err
}


jwt.go

package jwtex

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

type Data struct {
	Name   string
	Age    int
	Gender int
	jwt.RegisteredClaims
}

type Jwt interface {
	Sing(data jwt.Claims) (string, error)
	Verify(sign string, data jwt.Claims) error
}


https://github.com/0voice

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐