Help us shape the future of Go! Take the 2026 Go Developer Survey

jwtx

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package jwtx 提供独立的 HS256、RS256、ES256、PS256 JWT 签发器与验签器,复用 golang-jwt/jwt v5。 实例固定算法与密钥并可并发使用;声明的业务含义、刷新会话、撤销与 HTTP 适配由调用方负责。

以下示例需导入 crypto/rand、time、github.com/golang-jwt/jwt/v5 和 github.com/bpcoder16/pixiu/infra/jwtx。生产环境应从安全配置读取持久密钥, 下例临时生成密钥仅用于展示调用:

secret := make([]byte, 32)
if _, err := rand.Read(secret); err != nil {
	return err
}
signer, err := jwtx.NewSigner(jwtx.SignConfig{
	Algorithm: jwtx.HS256,
	Secret:    secret,
})
if err != nil {
	return err
}
verifier, err := jwtx.NewVerifier(jwtx.VerifyConfig{
	Algorithm: jwtx.HS256,
	Secret:    secret,
	Issuer:    "example",
	Audience:  []string{"app"},
})
if err != nil {
	return err
}
now := time.Now()
raw, err := signer.Sign(jwt.RegisteredClaims{
	Issuer:    "example",
	Subject:   "user-42",
	Audience:  jwt.ClaimStrings{"app"},
	IssuedAt:  jwt.NewNumericDate(now),
	ExpiresAt: jwt.NewNumericDate(now.Add(15 * time.Minute)),
})
if err != nil {
	return err
}
var claims jwt.RegisteredClaims
if err := verifier.Verify(raw, &claims); err != nil {
	return err
}
// 仅在 Verify 成功后使用 claims.Subject 等身份字段。
return nil

自定义 claims 推荐按值嵌入 jwt.RegisteredClaims,保留自身 JSON 布局。 Sign 要求 exp 存在但不修改或验证业务声明;Verify 始终要求 exp 并检查时间。 每次验签须使用新的空 claims 对象,出错时丢弃可能已解码的不可信内容。 可使用 errors.Is(err, jwt.ErrTokenExpired) 等判断底层错误,HTTP 错误码由应用映射。 HS256 密钥至少 32 字节;RS256、PS256 使用至少 2048 位的 RSA 密钥。 ES256 只接受 P-256,私钥支持 SEC1 / PKCS#8 PEM,公钥支持 PKIX / 证书 PEM。 PS256 沿用 RSA PEM 格式,签发和验签均要求标准的 32 字节盐长度。 非对称算法通过 PrivateKeyPEM / PublicKeyPEM 配置,不传 Secret;验签器只需公钥。 证书 PEM 仅提取公钥,不校验证书链或证书有效期。

Example
package main

import (
	"crypto/rand"
	"errors"
	"fmt"
	"time"

	"github.com/bpcoder16/pixiu/infra/jwtx"
	"github.com/golang-jwt/jwt/v5"
)

func main() {
	// 生产环境从安全配置读取持久密钥;这里临时生成用于展示。
	secret := make([]byte, 32)
	if _, err := rand.Read(secret); err != nil {
		panic(err)
	}
	signer, err := jwtx.NewSigner(jwtx.SignConfig{
		Algorithm: jwtx.HS256,
		Secret:    secret,
	})
	if err != nil {
		panic(err)
	}
	verifier, err := jwtx.NewVerifier(jwtx.VerifyConfig{
		Algorithm: jwtx.HS256,
		Secret:    secret,
		Issuer:    "example",
		Audience:  []string{"app"},
	})
	if err != nil {
		panic(err)
	}
	type appClaims struct {
		UserID string `json:"uid"`
		Portal string `json:"portal"`
		jwt.RegisteredClaims
	}
	now := time.Now()
	raw, err := signer.Sign(appClaims{
		UserID: "user-42",
		Portal: "app",
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    "example",
			Audience:  jwt.ClaimStrings{"app"},
			IssuedAt:  jwt.NewNumericDate(now),
			ExpiresAt: jwt.NewNumericDate(now.Add(15 * time.Minute)),
		},
	})
	if err != nil {
		panic(err)
	}
	var claims appClaims
	if err = verifier.Verify(raw, &claims); err != nil {
		if errors.Is(err, jwt.ErrTokenExpired) {
			fmt.Println("token 已过期")
		}
		return
	}
	fmt.Println(claims.UserID, claims.Portal)
}
Output:
user-42 app

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Algorithm

type Algorithm string

Algorithm 是实例固定使用的签名算法。 性能须分别比较签发和验签,并考虑 Go 版本、CPU、密钥长度及 claims 大小。 以下性能描述基于本机基准,环境与数据见 docs/jwtx-design.md;可运行 BenchmarkJWTAlgorithms 复测。

const (
	// HS256 使用 HMAC-SHA256;本机基准中签发、验签的耗时均最低。
	// 使用共享密钥,持有验签密钥的一方也具备签发能力。
	HS256 Algorithm = "HS256"

	// RS256 使用 RSA PKCS#1 v1.5;本机 2048 位 RSA 基准中签发成本明显高于验签。
	// RSA 密钥长度影响运算成本,不能将 2048 位基准直接用于更长的密钥。
	RS256 Algorithm = "RS256"

	// ES256 使用 P-256 ECDSA;本机基准中签发快于 2048 位 RSA,验签慢于 RSA。
	// 原始签名为 64 字节,小于 2048 位 RSA 的 256 字节,可减少 Token 传输体积。
	ES256 Algorithm = "ES256"

	// PS256 使用 RSA-PSS;同一把 2048 位 RSA 密钥下,本机签发、验签成本与 RS256 接近。
	// PSS 另有随机盐和编码处理,不应假定它与 RS256 存在固定的性能排序。
	PS256 Algorithm = "PS256"
)

type SignConfig

type SignConfig struct {
	// Algorithm 必填,无默认值,不根据密钥类型自动推断。
	// 支持 HS256(HMAC)、RS256(RSA PKCS#1 v1.5)、ES256(ECDSA)、PS256(RSA-PSS),均使用 SHA-256。
	Algorithm Algorithm

	// Secret 仅在 HS256 时必填,至少 32 字节;此时 PrivateKeyPEM 必须留空。
	// RS256、ES256、PS256 不接受 Secret,必须留空(nil 或长度为 0)。
	// 接收原始密钥字节;配置中若保存的是 Base64 或十六进制文本,须由调用方先解码。
	// 生产环境应使用安全随机生成并持久保存的密钥,启动时读取,不应每次启动重新生成。
	Secret []byte

	// PrivateKeyPEM 在 RS256、ES256、PS256 时必填;此时 Secret 必须留空。
	// HS256 不接受 PrivateKeyPEM,必须留空(nil 或长度为 0)。
	// RS256、PS256:RSA 私钥,至少 2048 位,支持 PKCS#1 / PKCS#8 PEM。
	// ES256:ECDSA 私钥,必须使用 P-256 曲线,支持 SEC1 / PKCS#8 PEM。
	// 接收完整、未加密的私钥 PEM 内容,不能传入公钥或文件路径。
	PrivateKeyPEM []byte
}

SignConfig 配置签发器;必须显式指定算法及其对应的密钥,不自动生成密钥。 密钥由调用方从安全配置或文件读取;字段接收密钥内容,不接受文件路径。 缺少必填字段、传入该算法不接受的非空密钥字段或密钥无效时,NewSigner 返回错误。

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer 是可并发复用的签发器;零值不可用,须经 NewSigner 创建。

func NewSigner

func NewSigner(cfg SignConfig) (*Signer, error)

NewSigner 校验配置并复制或解析密钥,不保留配置切片。

Example (Asymmetric)
package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"time"

	"github.com/bpcoder16/pixiu/infra/jwtx"
	"github.com/golang-jwt/jwt/v5"
)

func main() {
	// 示例生成临时密钥;实际应用从安全配置读取持久 PEM。
	ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		panic(err)
	}
	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		panic(err)
	}
	for _, config := range []struct {
		algorithm jwtx.Algorithm
		key       crypto.Signer
	}{
		{
			algorithm: jwtx.ES256,
			key:       ecKey,
		},
		{
			algorithm: jwtx.PS256,
			key:       rsaKey,
		},
	} {
		privateDER, err := x509.MarshalPKCS8PrivateKey(config.key)
		if err != nil {
			panic(err)
		}
		publicDER, err := x509.MarshalPKIXPublicKey(config.key.Public())
		if err != nil {
			panic(err)
		}
		signer, err := jwtx.NewSigner(jwtx.SignConfig{
			Algorithm: config.algorithm,
			PrivateKeyPEM: pem.EncodeToMemory(&pem.Block{
				Type:  "PRIVATE KEY",
				Bytes: privateDER,
			}),
		})
		if err != nil {
			panic(err)
		}
		verifier, err := jwtx.NewVerifier(jwtx.VerifyConfig{
			Algorithm: config.algorithm,
			PublicKeyPEM: pem.EncodeToMemory(&pem.Block{
				Type:  "PUBLIC KEY",
				Bytes: publicDER,
			}),
			Issuer:   "auth",
			Audience: []string{"app"},
		})
		if err != nil {
			panic(err)
		}
		raw, err := signer.Sign(jwt.RegisteredClaims{
			Issuer:    "auth",
			Subject:   "user-42",
			Audience:  jwt.ClaimStrings{"app"},
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
		})
		if err != nil {
			panic(err)
		}
		var claims jwt.RegisteredClaims
		if err = verifier.Verify(raw, &claims); err != nil {
			panic(err)
		}
		fmt.Println(config.algorithm, claims.Subject)
	}
}
Output:
ES256 user-42
PS256 user-42

func (*Signer) Sign

func (s *Signer) Sign(claims jwt.Claims) (string, error)

Sign 要求 exp 存在并原样签名,不补写声明或执行时间、业务校验。 调用期间不得修改 claims;自定义 getter 和 JSON 编码须由调用方保证正确。 jwt.MapClaims 的 exp 应使用 float64 或 json.Number 秒值,遵循上游 getter 契约。

type Verifier

type Verifier struct {
	// contains filtered or unexported fields
}

Verifier 是可并发复用的验签器;零值不可用,须经 NewVerifier 创建。

func NewVerifier

func NewVerifier(cfg VerifyConfig) (*Verifier, error)

NewVerifier 解析密钥并固定校验策略;证书 PEM 仅用于提取公钥。

func (*Verifier) Verify

func (v *Verifier) Verify(raw string, claims jwt.Claims) error

Verify 验证签名与声明;仅返回 nil 时 claims 才可信。 每次须传入新的空 claims 指针或已初始化的 jwt.MapClaims,不可并发共享目标。 失败时目标可能已写入不可信内容;raw 是 token 原文,不含 Bearer 前缀。

type VerifyConfig

type VerifyConfig struct {
	// Algorithm 必填,无默认值,不根据密钥类型或 token 中的 alg 自动推断。
	// 支持 HS256、RS256、ES256、PS256;token 中的 alg 必须与此配置精确匹配。
	Algorithm Algorithm

	// Secret 仅在 HS256 时必填,至少 32 字节;此时 PublicKeyPEM 必须留空。
	// 必须与签发器使用相同的原始密钥字节;Base64 或十六进制文本须由调用方先解码。
	// RS256、ES256、PS256 不接受 Secret,必须留空(nil 或长度为 0)。
	Secret []byte

	// PublicKeyPEM 在 RS256、ES256、PS256 时必填;此时 Secret 必须留空。
	// HS256 不接受 PublicKeyPEM,必须留空(nil 或长度为 0)。
	// RS256、PS256:RSA 公钥,至少 2048 位,支持 PKIX / PKCS#1 PEM。
	// ES256:ECDSA 公钥,必须使用 P-256 曲线,支持 PKIX PEM。
	// 均支持证书 PEM,但仅提取公钥,不校验证书链或证书有效期。
	// 接收与签发私钥配对的完整公钥或证书 PEM 内容,不能传入私钥或文件路径。
	PublicKeyPEM []byte

	// Issuer 非空时要求 iss 存在且精确匹配;为空时不校验签发者。
	// 不接受仅含空白的配置值,不自动去除首尾空白。
	Issuer string

	// Audience 非空时要求 aud 至少匹配其中一个值;为空时不校验接收方。
	// 每个元素均不得为空字符串或仅含空白,不自动去除首尾空白。
	Audience []string

	// Leeway 是时间校验允许的偏差,默认 0,不得为负。
	// 用于 exp、nbf,以及开启 CheckIssuedAt 后的 iat 校验,以容忍时钟偏差。
	Leeway time.Duration

	// CheckIssuedAt 默认 false;开启后检查已有 iat 不晚于当前时间(计入 Leeway)。
	// 不要求 iat 存在,也不会自动补写签发时间。
	CheckIssuedAt bool
}

VerifyConfig 配置验签器;exp 始终必需,算法与密钥在构造后固定。 密钥由调用方从安全配置或文件读取;字段接收密钥内容,不接受文件路径。 缺少必填字段、传入该算法不接受的非空密钥字段或配置无效时,NewVerifier 返回错误。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL