Tool_test.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package test
  2. import (
  3. "crypto/ed25519"
  4. "encoding/pem"
  5. "os"
  6. "testing"
  7. "time"
  8. "github.com/5-say/go-kit/gin/jwtx/tool"
  9. "github.com/golang-jwt/jwt/v5"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. )
  13. // TestGeneratePrivateKey 测试 GeneratePrivateKey 函数
  14. func TestGeneratePrivateKey(t *testing.T) {
  15. t.Run("生成有效的私钥", func(t *testing.T) {
  16. privateKey, err := tool.GeneratePrivateKey()
  17. require.NoError(t, err)
  18. assert.NotNil(t, privateKey)
  19. assert.Equal(t, ed25519.PrivateKeySize, len(*privateKey))
  20. })
  21. t.Run("生成的私钥可以用于签名", func(t *testing.T) {
  22. privateKey, err := tool.GeneratePrivateKey()
  23. require.NoError(t, err)
  24. // 测试私钥可以用于签名
  25. message := []byte("test message")
  26. signature := ed25519.Sign(*privateKey, message)
  27. // 验证签名
  28. publicKey := privateKey.Public().(ed25519.PublicKey)
  29. isValid := ed25519.Verify(publicKey, message, signature)
  30. assert.True(t, isValid)
  31. })
  32. }
  33. // TestGenerateToken 测试 GenerateToken 函数
  34. func TestGenerateToken(t *testing.T) {
  35. // 生成测试用的私钥
  36. privateKey, err := tool.GeneratePrivateKey()
  37. require.NoError(t, err)
  38. t.Run("生成有效的 JWT token", func(t *testing.T) {
  39. claims := jwt.MapClaims{
  40. "user_id": "12345",
  41. "role": "admin",
  42. "iss": "test-issuer",
  43. "exp": time.Now().Add(time.Hour).Unix(),
  44. "iat": time.Now().Unix(),
  45. }
  46. token, err := tool.GenerateToken(privateKey, claims)
  47. require.NoError(t, err)
  48. assert.NotEmpty(t, token)
  49. assert.Contains(t, token, ".") // JWT token 应该包含点分隔符
  50. })
  51. t.Run("生成的 token 可以被正确解析", func(t *testing.T) {
  52. claims := jwt.MapClaims{
  53. "user_id": "67890",
  54. "email": "test@example.com",
  55. "exp": time.Now().Add(time.Hour).Unix(),
  56. }
  57. token, err := tool.GenerateToken(privateKey, claims)
  58. require.NoError(t, err)
  59. // 使用公钥解析 token
  60. publicKey := privateKey.Public().(ed25519.PublicKey)
  61. parsedClaims, err := tool.ParseToken(token, &publicKey)
  62. require.NoError(t, err)
  63. assert.Equal(t, "67890", parsedClaims["user_id"])
  64. assert.Equal(t, "test@example.com", parsedClaims["email"])
  65. })
  66. t.Run("空声明生成 token", func(t *testing.T) {
  67. claims := jwt.MapClaims{}
  68. token, err := tool.GenerateToken(privateKey, claims)
  69. require.NoError(t, err)
  70. assert.NotEmpty(t, token)
  71. })
  72. }
  73. // TestParseToken 测试 ParseToken 函数
  74. func TestParseToken(t *testing.T) {
  75. // 生成测试用的密钥对
  76. privateKey, err := tool.GeneratePrivateKey()
  77. require.NoError(t, err)
  78. publicKey := privateKey.Public().(ed25519.PublicKey)
  79. t.Run("解析有效的 token", func(t *testing.T) {
  80. originalClaims := jwt.MapClaims{
  81. "user_id": "test-user",
  82. "scope": "read write",
  83. "exp": time.Now().Add(time.Hour).Unix(),
  84. }
  85. token, err := tool.GenerateToken(privateKey, originalClaims)
  86. require.NoError(t, err)
  87. parsedClaims, err := tool.ParseToken(token, &publicKey)
  88. require.NoError(t, err)
  89. assert.Equal(t, originalClaims["user_id"], parsedClaims["user_id"])
  90. assert.Equal(t, originalClaims["scope"], parsedClaims["scope"])
  91. })
  92. t.Run("解析无效的 token 应该失败", func(t *testing.T) {
  93. invalidToken := "invalid.jwt.token"
  94. _, err := tool.ParseToken(invalidToken, &publicKey)
  95. assert.Error(t, err)
  96. })
  97. t.Run("使用错误的公钥解析应该失败", func(t *testing.T) {
  98. // 生成另一个密钥对
  99. wrongPrivateKey, err := tool.GeneratePrivateKey()
  100. require.NoError(t, err)
  101. wrongPublicKey := wrongPrivateKey.Public().(ed25519.PublicKey)
  102. claims := jwt.MapClaims{"test": "value"}
  103. token, err := tool.GenerateToken(privateKey, claims)
  104. require.NoError(t, err)
  105. // 使用错误的公钥解析
  106. _, err = tool.ParseToken(token, &wrongPublicKey)
  107. assert.Error(t, err)
  108. })
  109. t.Run("解析过期的 token 应该失败", func(t *testing.T) {
  110. // 创建过期的 claims
  111. expiredClaims := jwt.MapClaims{
  112. "user_id": "expired-user",
  113. "exp": time.Now().Add(-time.Hour).Unix(), // 过去的时间
  114. }
  115. token, err := tool.GenerateToken(privateKey, expiredClaims)
  116. require.NoError(t, err)
  117. _, err = tool.ParseToken(token, &publicKey)
  118. assert.Error(t, err)
  119. assert.Contains(t, err.Error(), "expired")
  120. })
  121. }
  122. // TestGetPrivateKey 测试 GetPrivateKey 函数
  123. func TestGetPrivateKey(t *testing.T) {
  124. // 创建临时文件用于测试
  125. tempFile, err := os.CreateTemp("", "test_private_key_*.pem")
  126. require.NoError(t, err)
  127. defer os.Remove(tempFile.Name())
  128. t.Run("从有效的 PEM 文件加载私钥", func(t *testing.T) {
  129. // 生成私钥并保存到文件
  130. privateKey, err := tool.GeneratePrivateKey()
  131. require.NoError(t, err)
  132. // 将私钥保存为 PEM 格式
  133. pemData := pemEncodePrivateKey(*privateKey)
  134. err = os.WriteFile(tempFile.Name(), pemData, 0600)
  135. require.NoError(t, err)
  136. // 从文件加载私钥
  137. loadedKey, err := tool.GetPrivateKey(tempFile.Name())
  138. require.NoError(t, err)
  139. assert.NotNil(t, loadedKey)
  140. assert.Equal(t, *privateKey, *loadedKey)
  141. })
  142. t.Run("加载不存在的文件应该失败", func(t *testing.T) {
  143. _, err := tool.GetPrivateKey("/nonexistent/file.pem")
  144. assert.Error(t, err)
  145. })
  146. t.Run("加载无效的 PEM 文件应该失败", func(t *testing.T) {
  147. // 写入无效的 PEM 数据
  148. err := os.WriteFile(tempFile.Name(), []byte("invalid pem data"), 0600)
  149. require.NoError(t, err)
  150. _, err = tool.GetPrivateKey(tempFile.Name())
  151. assert.Error(t, err)
  152. })
  153. t.Run("加载非私钥的 PEM 文件应该失败", func(t *testing.T) {
  154. // 写入公钥 PEM 数据(应该失败)
  155. privateKey, err := tool.GeneratePrivateKey()
  156. require.NoError(t, err)
  157. publicKey := privateKey.Public().(ed25519.PublicKey)
  158. pemData := pemEncodePublicKey(publicKey)
  159. err = os.WriteFile(tempFile.Name(), pemData, 0600)
  160. require.NoError(t, err)
  161. _, err = tool.GetPrivateKey(tempFile.Name())
  162. assert.Error(t, err)
  163. })
  164. }
  165. // TestIntegration 集成测试:完整的 JWT 流程
  166. func TestIntegration(t *testing.T) {
  167. t.Run("完整的 JWT 生成和验证流程", func(t *testing.T) {
  168. // 1. 生成私钥
  169. privateKey, err := tool.GeneratePrivateKey()
  170. require.NoError(t, err)
  171. // 2. 创建 claims
  172. claims := jwt.MapClaims{
  173. "user_id": "integration-test",
  174. "name": "Test User",
  175. "email": "test@integration.com",
  176. "roles": []string{"user", "admin"},
  177. "exp": time.Now().Add(2 * time.Hour).Unix(),
  178. "iat": time.Now().Unix(),
  179. "iss": "integration-test-server",
  180. }
  181. // 3. 生成 JWT token
  182. token, err := tool.GenerateToken(privateKey, claims)
  183. require.NoError(t, err)
  184. assert.NotEmpty(t, token)
  185. // 4. 获取公钥
  186. publicKey := privateKey.Public().(ed25519.PublicKey)
  187. // 5. 解析和验证 token
  188. parsedClaims, err := tool.ParseToken(token, &publicKey)
  189. require.NoError(t, err)
  190. // 6. 验证 claims 内容
  191. assert.Equal(t, claims["user_id"], parsedClaims["user_id"])
  192. assert.Equal(t, claims["name"], parsedClaims["name"])
  193. assert.Equal(t, claims["email"], parsedClaims["email"])
  194. assert.Equal(t, claims["iss"], parsedClaims["iss"])
  195. // 7. 验证 token 可以用于实际场景
  196. // 例如:在中间件中验证 token 的有效性
  197. _, err = tool.ParseToken(token, &publicKey)
  198. assert.NoError(t, err, "token 应该可以重复验证")
  199. })
  200. }
  201. // pemEncodePrivateKey 将 Ed25519 私钥编码为 PEM 格式
  202. func pemEncodePrivateKey(privateKey ed25519.PrivateKey) []byte {
  203. // 注意:ed25519.PrivateKey 实际上是 []byte,可以直接作为 PEM 数据
  204. block := &pem.Block{
  205. Type: "PRIVATE KEY",
  206. Bytes: privateKey,
  207. }
  208. return pem.EncodeToMemory(block)
  209. }
  210. // pemEncodePublicKey 将 Ed25519 公钥编码为 PEM 格式
  211. func pemEncodePublicKey(publicKey ed25519.PublicKey) []byte {
  212. block := &pem.Block{
  213. Type: "PUBLIC KEY",
  214. Bytes: publicKey,
  215. }
  216. return pem.EncodeToMemory(block)
  217. }