jwt_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package auth
  2. import (
  3. "testing"
  4. "time"
  5. "perms-system-server/internal/middleware"
  6. "github.com/golang-jwt/jwt/v4"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. )
  10. const testSecret = "test-jwt-secret-key"
  11. // TC-0217: secret="s", expire=3600, userId=1, username="u", productCode="p", memberType="M", perms=["a"]
  12. func TestGenerateAccessToken(t *testing.T) {
  13. tests := []struct {
  14. name string
  15. secret string
  16. expire int64
  17. userId int64
  18. username string
  19. productCode string
  20. memberType string
  21. perms []string
  22. }{
  23. {
  24. name: "normal generation",
  25. secret: testSecret,
  26. expire: 3600,
  27. userId: 1,
  28. username: "admin",
  29. productCode: "p1",
  30. memberType: "ADMIN",
  31. perms: []string{"perm_a", "perm_b"},
  32. },
  33. {
  34. name: "empty perms",
  35. secret: testSecret,
  36. expire: 3600,
  37. userId: 2,
  38. username: "user1",
  39. productCode: "p1",
  40. memberType: "MEMBER",
  41. perms: nil,
  42. },
  43. {
  44. name: "empty productCode",
  45. secret: testSecret,
  46. expire: 3600,
  47. userId: 3,
  48. username: "user2",
  49. productCode: "",
  50. memberType: "",
  51. perms: []string{},
  52. },
  53. {
  54. name: "super admin",
  55. secret: testSecret,
  56. expire: 7200,
  57. userId: 100,
  58. username: "super",
  59. productCode: "p1",
  60. memberType: "SUPER_ADMIN",
  61. perms: []string{"all_perm_1", "all_perm_2", "all_perm_3"},
  62. },
  63. }
  64. for _, tt := range tests {
  65. t.Run(tt.name, func(t *testing.T) {
  66. tokenStr, err := GenerateAccessToken(tt.secret, tt.expire, tt.userId, tt.username, tt.productCode, tt.memberType, tt.perms)
  67. require.NoError(t, err)
  68. assert.NotEmpty(t, tokenStr)
  69. token, err := jwt.ParseWithClaims(tokenStr, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) {
  70. return []byte(tt.secret), nil
  71. })
  72. require.NoError(t, err)
  73. assert.True(t, token.Valid)
  74. claims, ok := token.Claims.(*middleware.Claims)
  75. require.True(t, ok)
  76. assert.Equal(t, tt.userId, claims.UserId)
  77. assert.Equal(t, tt.username, claims.Username)
  78. assert.Equal(t, tt.productCode, claims.ProductCode)
  79. assert.Equal(t, tt.memberType, claims.MemberType)
  80. if tt.perms == nil {
  81. assert.Nil(t, claims.Perms)
  82. } else {
  83. assert.Equal(t, tt.perms, claims.Perms)
  84. }
  85. })
  86. }
  87. }
  88. // TC-0221: expireSeconds=1, sleep 2s
  89. func TestGenerateAccessToken_Expiry(t *testing.T) {
  90. tokenStr, err := GenerateAccessToken(testSecret, 1, 1, "u", "", "", nil)
  91. require.NoError(t, err)
  92. time.Sleep(2 * time.Second)
  93. _, err = jwt.ParseWithClaims(tokenStr, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) {
  94. return []byte(testSecret), nil
  95. })
  96. assert.Error(t, err)
  97. assert.Contains(t, err.Error(), "token is expired")
  98. }
  99. // TC-0222: secret="s", expire=86400, userId=1, productCode="p"
  100. func TestGenerateRefreshToken(t *testing.T) {
  101. tests := []struct {
  102. name string
  103. secret string
  104. expire int64
  105. userId int64
  106. productCode string
  107. }{
  108. {"normal", testSecret, 86400, 1, "p1"},
  109. {"empty productCode", testSecret, 86400, 2, ""},
  110. }
  111. for _, tt := range tests {
  112. t.Run(tt.name, func(t *testing.T) {
  113. tokenStr, err := GenerateRefreshToken(tt.secret, tt.expire, tt.userId, tt.productCode)
  114. require.NoError(t, err)
  115. assert.NotEmpty(t, tokenStr)
  116. claims, err := ParseRefreshToken(tokenStr, tt.secret)
  117. require.NoError(t, err)
  118. assert.Equal(t, tt.userId, claims.UserId)
  119. assert.Equal(t, tt.productCode, claims.ProductCode)
  120. })
  121. }
  122. }
  123. // TC-0225: 有效token+正确secret
  124. func TestParseRefreshToken(t *testing.T) {
  125. validToken, err := GenerateRefreshToken(testSecret, 3600, 42, "prod")
  126. require.NoError(t, err)
  127. t.Run("valid token", func(t *testing.T) {
  128. claims, err := ParseRefreshToken(validToken, testSecret)
  129. require.NoError(t, err)
  130. assert.Equal(t, int64(42), claims.UserId)
  131. assert.Equal(t, "prod", claims.ProductCode)
  132. })
  133. t.Run("wrong secret", func(t *testing.T) {
  134. _, err := ParseRefreshToken(validToken, "wrong-secret")
  135. assert.Error(t, err)
  136. })
  137. t.Run("invalid token string", func(t *testing.T) {
  138. _, err := ParseRefreshToken("not-a-valid-token", testSecret)
  139. assert.Error(t, err)
  140. })
  141. t.Run("empty token", func(t *testing.T) {
  142. _, err := ParseRefreshToken("", testSecret)
  143. assert.Error(t, err)
  144. })
  145. t.Run("expired token", func(t *testing.T) {
  146. expiredToken, err := GenerateRefreshToken(testSecret, 1, 1, "p")
  147. require.NoError(t, err)
  148. time.Sleep(2 * time.Second)
  149. _, err = ParseRefreshToken(expiredToken, testSecret)
  150. assert.Error(t, err)
  151. })
  152. // TC-0230: AccessToken误用 — TokenType校验拒绝
  153. t.Run("access token used as refresh - should be rejected", func(t *testing.T) {
  154. accessToken, err := GenerateAccessToken(testSecret, 3600, 1, "u", "p", "M", []string{"a"})
  155. require.NoError(t, err)
  156. _, err = ParseRefreshToken(accessToken, testSecret)
  157. assert.Error(t, err, "BUG-002: access token 不应被 ParseRefreshToken 接受,应通过 TokenType 字段区分")
  158. })
  159. }
  160. // TC-0219: secret=""
  161. func TestGenerateAccessToken_EmptySecret(t *testing.T) {
  162. tokenStr, err := GenerateAccessToken("", 3600, 1, "u", "p", "M", []string{"a"})
  163. require.NoError(t, err)
  164. assert.NotEmpty(t, tokenStr)
  165. token, err := jwt.ParseWithClaims(tokenStr, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) {
  166. return []byte(""), nil
  167. })
  168. require.NoError(t, err)
  169. assert.True(t, token.Valid)
  170. claims, ok := token.Claims.(*middleware.Claims)
  171. require.True(t, ok)
  172. assert.Equal(t, int64(1), claims.UserId)
  173. }