| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- package test
- import (
- "crypto/ed25519"
- "encoding/pem"
- "os"
- "testing"
- "time"
- "git.ooo.ink/root/go-kit/gin/jwtx/tool"
- "github.com/golang-jwt/jwt/v5"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- // TestGeneratePrivateKey 测试 GeneratePrivateKey 函数
- func TestGeneratePrivateKey(t *testing.T) {
- t.Run("生成有效的私钥", func(t *testing.T) {
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- assert.NotNil(t, privateKey)
- assert.Equal(t, ed25519.PrivateKeySize, len(*privateKey))
- })
- t.Run("生成的私钥可以用于签名", func(t *testing.T) {
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- // 测试私钥可以用于签名
- message := []byte("test message")
- signature := ed25519.Sign(*privateKey, message)
- // 验证签名
- publicKey := privateKey.Public().(ed25519.PublicKey)
- isValid := ed25519.Verify(publicKey, message, signature)
- assert.True(t, isValid)
- })
- }
- // TestGenerateToken 测试 GenerateToken 函数
- func TestGenerateToken(t *testing.T) {
- // 生成测试用的私钥
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- t.Run("生成有效的 JWT token", func(t *testing.T) {
- claims := jwt.MapClaims{
- "user_id": "12345",
- "role": "admin",
- "iss": "test-issuer",
- "exp": time.Now().Add(time.Hour).Unix(),
- "iat": time.Now().Unix(),
- }
- token, err := tool.GenerateToken(privateKey, claims)
- require.NoError(t, err)
- assert.NotEmpty(t, token)
- assert.Contains(t, token, ".") // JWT token 应该包含点分隔符
- })
- t.Run("生成的 token 可以被正确解析", func(t *testing.T) {
- claims := jwt.MapClaims{
- "user_id": "67890",
- "email": "test@example.com",
- "exp": time.Now().Add(time.Hour).Unix(),
- }
- token, err := tool.GenerateToken(privateKey, claims)
- require.NoError(t, err)
- // 使用公钥解析 token
- publicKey := privateKey.Public().(ed25519.PublicKey)
- parsedClaims, err := tool.ParseToken(token, &publicKey)
- require.NoError(t, err)
- assert.Equal(t, "67890", parsedClaims["user_id"])
- assert.Equal(t, "test@example.com", parsedClaims["email"])
- })
- t.Run("空声明生成 token", func(t *testing.T) {
- claims := jwt.MapClaims{}
- token, err := tool.GenerateToken(privateKey, claims)
- require.NoError(t, err)
- assert.NotEmpty(t, token)
- })
- }
- // TestParseToken 测试 ParseToken 函数
- func TestParseToken(t *testing.T) {
- // 生成测试用的密钥对
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- publicKey := privateKey.Public().(ed25519.PublicKey)
- t.Run("解析有效的 token", func(t *testing.T) {
- originalClaims := jwt.MapClaims{
- "user_id": "test-user",
- "scope": "read write",
- "exp": time.Now().Add(time.Hour).Unix(),
- }
- token, err := tool.GenerateToken(privateKey, originalClaims)
- require.NoError(t, err)
- parsedClaims, err := tool.ParseToken(token, &publicKey)
- require.NoError(t, err)
- assert.Equal(t, originalClaims["user_id"], parsedClaims["user_id"])
- assert.Equal(t, originalClaims["scope"], parsedClaims["scope"])
- })
- t.Run("解析无效的 token 应该失败", func(t *testing.T) {
- invalidToken := "invalid.jwt.token"
- _, err := tool.ParseToken(invalidToken, &publicKey)
- assert.Error(t, err)
- })
- t.Run("使用错误的公钥解析应该失败", func(t *testing.T) {
- // 生成另一个密钥对
- wrongPrivateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- wrongPublicKey := wrongPrivateKey.Public().(ed25519.PublicKey)
- claims := jwt.MapClaims{"test": "value"}
- token, err := tool.GenerateToken(privateKey, claims)
- require.NoError(t, err)
- // 使用错误的公钥解析
- _, err = tool.ParseToken(token, &wrongPublicKey)
- assert.Error(t, err)
- })
- t.Run("解析过期的 token 应该失败", func(t *testing.T) {
- // 创建过期的 claims
- expiredClaims := jwt.MapClaims{
- "user_id": "expired-user",
- "exp": time.Now().Add(-time.Hour).Unix(), // 过去的时间
- }
- token, err := tool.GenerateToken(privateKey, expiredClaims)
- require.NoError(t, err)
- _, err = tool.ParseToken(token, &publicKey)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "expired")
- })
- }
- // TestGetPrivateKey 测试 GetPrivateKey 函数
- func TestGetPrivateKey(t *testing.T) {
- // 创建临时文件用于测试
- tempFile, err := os.CreateTemp("", "test_private_key_*.pem")
- require.NoError(t, err)
- defer os.Remove(tempFile.Name())
- t.Run("从有效的 PEM 文件加载私钥", func(t *testing.T) {
- // 生成私钥并保存到文件
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- // 将私钥保存为 PEM 格式
- pemData := pemEncodePrivateKey(*privateKey)
- err = os.WriteFile(tempFile.Name(), pemData, 0600)
- require.NoError(t, err)
- // 从文件加载私钥
- loadedKey, err := tool.GetPrivateKey(tempFile.Name())
- require.NoError(t, err)
- assert.NotNil(t, loadedKey)
- assert.Equal(t, *privateKey, *loadedKey)
- })
- t.Run("加载不存在的文件应该失败", func(t *testing.T) {
- _, err := tool.GetPrivateKey("/nonexistent/file.pem")
- assert.Error(t, err)
- })
- t.Run("加载无效的 PEM 文件应该失败", func(t *testing.T) {
- // 写入无效的 PEM 数据
- err := os.WriteFile(tempFile.Name(), []byte("invalid pem data"), 0600)
- require.NoError(t, err)
- _, err = tool.GetPrivateKey(tempFile.Name())
- assert.Error(t, err)
- })
- t.Run("加载非私钥的 PEM 文件应该失败", func(t *testing.T) {
- // 写入公钥 PEM 数据(应该失败)
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- publicKey := privateKey.Public().(ed25519.PublicKey)
- pemData := pemEncodePublicKey(publicKey)
- err = os.WriteFile(tempFile.Name(), pemData, 0600)
- require.NoError(t, err)
- _, err = tool.GetPrivateKey(tempFile.Name())
- assert.Error(t, err)
- })
- }
- // TestIntegration 集成测试:完整的 JWT 流程
- func TestIntegration(t *testing.T) {
- t.Run("完整的 JWT 生成和验证流程", func(t *testing.T) {
- // 1. 生成私钥
- privateKey, err := tool.GeneratePrivateKey()
- require.NoError(t, err)
- // 2. 创建 claims
- claims := jwt.MapClaims{
- "user_id": "integration-test",
- "name": "Test User",
- "email": "test@integration.com",
- "roles": []string{"user", "admin"},
- "exp": time.Now().Add(2 * time.Hour).Unix(),
- "iat": time.Now().Unix(),
- "iss": "integration-test-server",
- }
- // 3. 生成 JWT token
- token, err := tool.GenerateToken(privateKey, claims)
- require.NoError(t, err)
- assert.NotEmpty(t, token)
- // 4. 获取公钥
- publicKey := privateKey.Public().(ed25519.PublicKey)
- // 5. 解析和验证 token
- parsedClaims, err := tool.ParseToken(token, &publicKey)
- require.NoError(t, err)
- // 6. 验证 claims 内容
- assert.Equal(t, claims["user_id"], parsedClaims["user_id"])
- assert.Equal(t, claims["name"], parsedClaims["name"])
- assert.Equal(t, claims["email"], parsedClaims["email"])
- assert.Equal(t, claims["iss"], parsedClaims["iss"])
- // 7. 验证 token 可以用于实际场景
- // 例如:在中间件中验证 token 的有效性
- _, err = tool.ParseToken(token, &publicKey)
- assert.NoError(t, err, "token 应该可以重复验证")
- })
- }
- // pemEncodePrivateKey 将 Ed25519 私钥编码为 PEM 格式
- func pemEncodePrivateKey(privateKey ed25519.PrivateKey) []byte {
- // 注意:ed25519.PrivateKey 实际上是 []byte,可以直接作为 PEM 数据
- block := &pem.Block{
- Type: "PRIVATE KEY",
- Bytes: privateKey,
- }
- return pem.EncodeToMemory(block)
- }
- // pemEncodePublicKey 将 Ed25519 公钥编码为 PEM 格式
- func pemEncodePublicKey(publicKey ed25519.PublicKey) []byte {
- block := &pem.Block{
- Type: "PUBLIC KEY",
- Bytes: publicKey,
- }
- return pem.EncodeToMemory(block)
- }
|