golang实现平台无关安全令牌(PASETO)的插件库paseto的使用

Golang实现平台无关安全令牌(PASETO)的插件库paseto的使用

什么是PASETO?

PASETO(Platform-Agnostic SEcurity TOkens)是一种用于安全无状态令牌的规范和参考实现。

PASETO与JWT的主要区别

与JSON Web Tokens(JWT)不同,PASETO只允许安全操作。JWT提供"算法灵活性",而PASETO提供"版本化协议"。

安装

要安装该库,请使用以下命令:

go get -u github.com/o1egl/paseto

使用示例

使用对称密钥创建令牌(本地模式)

symmetricKey := []byte("YELLOW SUBMARINE, BLACK WIZARDRY") // 必须是32字节
now := time.Now()
exp := now.Add(24 * time.Hour)
nbt := now

jsonToken := paseto.JSONToken{
    Audience:   "test",
    Issuer:     "test_service",
    Jti:        "123",
    Subject:    "test_subject",
    IssuedAt:   now,
    Expiration: exp,
    NotBefore:  nbt,
}

// 添加自定义声明到令牌
jsonToken.Set("data", "this is a signed message")
footer := "some footer"

// 加密数据
token, err := paseto.Encrypt(symmetricKey, jsonToken, footer)
// token = "v2.local.E42A2iMY9SaZVzt-WkCi45_aebky4vbSUJsfG45OcanamwXwieieMjSjUkgsyZzlbYt82miN1xD-X0zEIhLK_RhWUPLZc9nC0shmkkkHS5Exj2zTpdNWhrC5KJRyUrI0cupc5qrctuREFLAvdCgwZBjh1QSgBX74V631fzl1IErGBgnt2LV1aij5W3hw9cXv4gtm_jSwsfee9HZcCE0sgUgAvklJCDO__8v_fTY7i_Regp5ZPa7h0X0m3yf0n4OXY9PRplunUpD9uEsXJ_MTF5gSFR3qE29eCHbJtRt0FFl81x-GCsQ9H9701TzEjGehCC6Bhw.c29tZSBmb290ZXI"

// 解密数据
var newJsonToken paseto.JSONToken
var newFooter string
err := paseto.Decrypt(token, symmetricKey, &newJsonToken, &newFooter)

使用非对称密钥创建令牌(公共模式)

b, _ := hex.DecodeString("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
privateKey := ed25519.PrivateKey(b)

b, _ = hex.DecodeString("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
publicKey := ed25519.PublicKey(b)

// 或者创建一个新的密钥对
// publicKey, privateKey, err := ed25519.GenerateKey(nil)

jsonToken := paseto.JSONToken{
    Expiration: time.Now().Add(24 * time.Hour),
}

// 添加自定义声明到令牌
jsonToken.Set("data", "this is a signed message")
footer := "some footer"

// 签名数据
token, err := paseto.Sign(privateKey, jsonToken, footer)
// token = "v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOC0wMy0xMlQxOTowODo1NCswMTowMCJ9Ojv0uXlUNXSFhR88KXb568LheLRdeGy2oILR3uyOM_-b7r7i_fX8aljFYUiF-MRr5IRHMBcWPtM0fmn9SOd6Aw.c29tZSBmb290ZXI"

// 验证数据
var newJsonToken paseto.JSONToken
var newFooter string
err := paseto.Verify(token, publicKey, &newJsonToken, &newFooter)

使用Parse()函数解析所有支持的令牌版本

b, err := hex.DecodeString("2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b43415145417878636e47724e4f6136426c41523458707050640d0a746146576946386f7279746c4b534d6a66446831314c687956627a4335416967556b706a457274394d7649482f46384d444a72324f39486b36594b454b574b6f0d0a72333566364b6853303679357a714f722b7a4e34312b39626a52365633322b527345776d5a737a3038375258764e41334e687242633264593647736e57336c5a0d0a34356f5341564a755639553667335a334a574138355972362b6350776134793755632f56726f6d7a674679627355656e33476f724254626a783142384f514a440d0a73652f4b6b6855433655693358384264514f473974523455454775742f6c39703970732b3661474d4c57694357495a54615456784d4f75653133596b777038740d0a3148467635747a6872493055635948687638464a6b315a6435386759464158634e797975737834346e6a6152594b595948646e6b4f6a486e33416b534c4d306b0d0a6c774944415141420d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d")
block, _ := pem.Decode(b)
rsaPubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
v1PublicKey := rsaPubInterface.(*rsa.PublicKey)

b, _ = hex.DecodeString("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
v2PublicKey := ed25519.PublicKey(b)

var payload JSONToken
var footer string
version, err := paseto.Parse(token, &payload, &footer, symmetricKey, map[paseto.Version]crypto.PublicKey{paseto.V1: v1PublicKey, paseto.V2: v2PublicKey})

支持的PASETO版本

版本2

版本2(规范推荐版本)完全支持。

版本1

版本1(兼容版本)完全支持。

性能基准

MacBook Pro (15-inch, 2018) CPU: 2,6 GHz Intel Core i7 RAM: 32 GB 2400 MHz DDR4 OS: macOS 10.14.6 GO: 1.13.7

$ go test -bench . -benchmem

Benchmark_V2_JSONToken_Encrypt-12         137578              8532 ns/op            4186 B/op         59 allocs/op
Benchmark_V2_JSONToken_Decrypt-12         139309              7970 ns/op            2048 B/op         63 allocs/op
Benchmark_V2_JSONToken_Sign-12             21598             55817 ns/op            4426 B/op         60 allocs/op
Benchmark_V2_JSONToken_Verify-12            8772            132142 ns/op            2528 B/op         64 allocs/op
Benchmark_V2_String_Encrypt-12            544958              2051 ns/op            1176 B/op         23 allocs/op
Benchmark_V2_String_Decrypt-12           1000000              1054 ns/op             568 B/op         18 allocs/op
Benchmark_V2_String_Sign-12                25144             47645 ns/op            1144 B/op         23 allocs/op
Benchmark_V2_String_Verify-12               9408            125524 ns/op             744 B/op         18 allocs/op

更多关于golang实现平台无关安全令牌(PASETO)的插件库paseto的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现平台无关安全令牌(PASETO)的插件库paseto的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用Go实现PASETO安全令牌

PASETO(Platform-Agnostic Security Tokens)是一种现代的安全令牌格式,相比JWT具有更简单的设计和更强的安全性。下面我将介绍如何在Go中使用paseto库来实现PASETO令牌的创建和验证。

安装paseto库

首先安装Go的paseto实现库:

go get github.com/o1egl/paseto

基本用法示例

1. 创建v2版本的本地PASETO令牌

package main

import (
	"fmt"
	"time"

	"github.com/o1egl/paseto"
	"golang.org/x/crypto/chacha20poly1305"
)

func main() {
	// 生成对称密钥(32字节)
	symmetricKey := make([]byte, chacha20poly1305.KeySize)
	if _, err := rand.Read(symmetricKey); err != nil {
		panic(err)
	}

	// 创建PASETO v2对象
	jsonToken := paseto.JSONToken{
		Expiration: time.Now().Add(24 * time.Hour),
		IssuedAt:   time.Now(),
		NotBefore:  time.Now(),
		Subject:    "user123",
	}
	
	// 添加自定义声明
	jsonToken.Set("data", "custom data")
	
	// 创建token
	token, err := paseto.NewV2().Encrypt(symmetricKey, jsonToken, nil)
	if err != nil {
		panic(err)
	}
	
	fmt.Println("Generated PASETO token:", token)
}

2. 验证v2本地PASETO令牌

func verifyToken(token string, symmetricKey []byte) {
	var newJSONToken paseto.JSONToken
	var newFooter string
	
	// 验证token
	err := paseto.NewV2().Decrypt(token, symmetricKey, &newJSONToken, &newFooter)
	if err != nil {
		fmt.Println("Token verification failed:", err)
		return
	}
	
	// 检查过期时间
	if time.Now().After(newJSONToken.Expiration) {
		fmt.Println("Token expired")
		return
	}
	
	fmt.Printf("Token verified! Subject: %s\n", newJSONToken.Subject)
	fmt.Printf("Custom claim 'data': %s\n", newJSONToken.Get("data"))
}

3. 使用v2公共令牌(非对称加密)

func createPublicToken() {
	// 生成RSA密钥对
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		panic(err)
	}
	publicKey := &privateKey.PublicKey

	// 创建token
	jsonToken := paseto.JSONToken{
		Expiration: time.Now().Add(2 * time.Hour),
		Issuer:     "myapp",
		Subject:    "user456",
	}
	
	token, err := paseto.NewV2().Sign(privateKey, jsonToken, nil)
	if err != nil {
		panic(err)
	}
	
	fmt.Println("Public PASETO token:", token)
	
	// 验证token
	var verifiedJSONToken paseto.JSONToken
	err = paseto.NewV2().Verify(token, publicKey, &verifiedJSONToken, nil)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("Verified token for user: %s\n", verifiedJSONToken.Subject)
}

最佳实践建议

  1. 密钥管理

    • 本地令牌使用强随机生成的密钥
    • 公共令牌使用足够强度的RSA或Ed25519密钥
    • 定期轮换密钥
  2. 令牌有效期

    • 设置合理的过期时间(通常几小时)
    • 使用NotBefore防止令牌在发布后立即使用
  3. 自定义声明

    • 避免存储敏感信息
    • 保持令牌尽可能小
  4. 错误处理

    • 区分不同类型的验证错误(过期、签名无效等)
    • 记录但不泄露详细的错误信息给客户端

与JWT的比较

PASETO相比JWT有几个优势:

  • 更简单的设计,减少了实现错误
  • 强制使用现代加密算法
  • 没有算法协商攻击的风险
  • 更小的令牌大小

完整示例

package main

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

	"github.com/o1egl/paseto"
	"golang.org/x/crypto/chacha20poly1305"
)

func main() {
	// 示例1: 本地令牌
	fmt.Println("=== Local Token Example ===")
	localTokenExample()
	
	// 示例2: 公共令牌
	fmt.Println("\n=== Public Token Example ===")
	publicTokenExample()
}

func localTokenExample() {
	// 生成32字节对称密钥
	symmetricKey := make([]byte, chacha20poly1305.KeySize)
	if _, err := rand.Read(symmetricKey); err != nil {
		panic(err)
	}

	// 创建token
	jsonToken := paseto.JSONToken{
		Expiration: time.Now().Add(1 * time.Hour),
		Issuer:     "auth-service",
		Subject:    "user789",
	}
	jsonToken.Set("role", "admin")

	token, err := paseto.NewV2().Encrypt(symmetricKey, jsonToken, nil)
	if err != nil {
		panic(err)
	}

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

	// 验证token
	var verifiedToken paseto.JSONToken
	err = paseto.NewV2().Decrypt(token, symmetricKey, &verifiedToken, nil)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Verified token - Subject: %s, Role: %s\n", 
		verifiedToken.Subject, verifiedToken.Get("role"))
}

func publicTokenExample() {
	// 生成RSA密钥对
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		panic(err)
	}
	publicKey := &privateKey.PublicKey

	// 创建token
	jsonToken := paseto.JSONToken{
		Expiration: time.Now().Add(2 * time.Hour),
		IssuedAt:   time.Now(),
		Subject:    "service-account",
	}
	jsonToken.Set("scope", "read:data,write:data")

	token, err := paseto.NewV2().Sign(privateKey, jsonToken, "key-id-123")
	if err != nil {
		panic(err)
	}

	fmt.Println("Generated public token:", token)

	// 验证token
	var verifiedToken paseto.JSONToken
	var footer string
	err = paseto.NewV2().Verify(token, publicKey, &verifiedToken, &footer)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Verified token - Subject: %s, Scope: %s, Footer: %s\n",
		verifiedToken.Subject, verifiedToken.Get("scope"), footer)
}

这个示例展示了PASETO的核心功能,包括本地和公共令牌的创建与验证。根据你的具体需求,可以调整令牌的有效期、声明内容和密钥管理策略。

回到顶部