refreshTokenLogic_test.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. package pub
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "testing"
  7. "time"
  8. authHelper "perms-system-server/internal/logic/auth"
  9. permModel "perms-system-server/internal/model/perm"
  10. productmemberModel "perms-system-server/internal/model/productmember"
  11. userModel "perms-system-server/internal/model/user"
  12. "perms-system-server/internal/response"
  13. "perms-system-server/internal/testutil"
  14. "perms-system-server/internal/types"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. func insertRefreshTestUser(t *testing.T, ctx context.Context, username, password string, status, isSuperAdmin int64) (int64, func()) {
  19. t.Helper()
  20. svcCtx := newTestSvcCtx()
  21. conn := testutil.GetTestSqlConn()
  22. now := time.Now().Unix()
  23. hashed := testutil.HashPassword(password)
  24. res, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  25. Username: username,
  26. Password: hashed,
  27. Nickname: username,
  28. Avatar: sql.NullString{},
  29. Email: username + "@test.com",
  30. Phone: "13800000000",
  31. Remark: "",
  32. DeptId: 0,
  33. IsSuperAdmin: isSuperAdmin,
  34. MustChangePassword: 2,
  35. Status: status,
  36. CreateTime: now,
  37. UpdateTime: now,
  38. })
  39. require.NoError(t, err)
  40. id, _ := res.LastInsertId()
  41. cleanup := func() {
  42. testutil.CleanTable(ctx, conn, "`sys_user`", id)
  43. }
  44. return id, cleanup
  45. }
  46. // TC-0025: 正常刷新(refreshToken从header获取,原样返回不重新生成)
  47. func TestRefreshToken_Normal(t *testing.T) {
  48. ctx := context.Background()
  49. svcCtx := newTestSvcCtx()
  50. username := testutil.UniqueId()
  51. password := "TestPass123"
  52. userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
  53. t.Cleanup(cleanUser)
  54. refreshToken, err := authHelper.GenerateRefreshToken(
  55. svcCtx.Config.Auth.RefreshSecret,
  56. svcCtx.Config.Auth.RefreshExpire,
  57. userId, "", 0,
  58. )
  59. require.NoError(t, err)
  60. logic := NewRefreshTokenLogic(ctx, svcCtx)
  61. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  62. Authorization: "Bearer " + refreshToken,
  63. })
  64. require.NoError(t, err)
  65. require.NotNil(t, resp)
  66. assert.NotEmpty(t, resp.AccessToken)
  67. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回,不重新生成")
  68. assert.NotEqual(t, resp.AccessToken, resp.RefreshToken, "accessToken和refreshToken应不同")
  69. assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
  70. assert.Equal(t, userId, resp.UserInfo.UserId)
  71. assert.Equal(t, username, resp.UserInfo.Username)
  72. }
  73. // TC-0026: 不带productCode(回退)
  74. func TestRefreshToken_FallbackToClaimsProductCode(t *testing.T) {
  75. ctx := context.Background()
  76. svcCtx := newTestSvcCtx()
  77. conn := testutil.GetTestSqlConn()
  78. username := testutil.UniqueId()
  79. password := "TestPass123"
  80. pc := testutil.UniqueId()
  81. now := time.Now().Unix()
  82. userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
  83. t.Cleanup(cleanUser)
  84. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  85. t.Cleanup(cleanProduct)
  86. pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &productmemberModel.SysProductMember{
  87. ProductCode: pc, UserId: userId, MemberType: "ADMIN", Status: 1, CreateTime: now, UpdateTime: now,
  88. })
  89. require.NoError(t, err)
  90. pmId, _ := pmRes.LastInsertId()
  91. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId) })
  92. permCode := testutil.UniqueId()
  93. permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  94. ProductCode: pc, Name: "refresh_perm", Code: permCode, Status: 1, CreateTime: now, UpdateTime: now,
  95. })
  96. require.NoError(t, err)
  97. permId, _ := permRes.LastInsertId()
  98. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
  99. refreshToken, err := authHelper.GenerateRefreshToken(
  100. svcCtx.Config.Auth.RefreshSecret,
  101. svcCtx.Config.Auth.RefreshExpire,
  102. userId, pc, 0,
  103. )
  104. require.NoError(t, err)
  105. logic := NewRefreshTokenLogic(ctx, svcCtx)
  106. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  107. Authorization: "Bearer " + refreshToken,
  108. })
  109. require.NoError(t, err)
  110. require.NotNil(t, resp)
  111. assert.Equal(t, "ADMIN", resp.UserInfo.MemberType)
  112. assert.Contains(t, resp.UserInfo.Perms, permCode)
  113. }
  114. // TC-0027: token无效
  115. func TestRefreshToken_InvalidToken(t *testing.T) {
  116. ctx := context.Background()
  117. svcCtx := newTestSvcCtx()
  118. logic := NewRefreshTokenLogic(ctx, svcCtx)
  119. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  120. Authorization: "Bearer invalid.token.string",
  121. })
  122. require.Nil(t, resp)
  123. require.Error(t, err)
  124. var codeErr *response.CodeError
  125. require.True(t, errors.As(err, &codeErr))
  126. assert.Equal(t, 401, codeErr.Code())
  127. assert.Equal(t, "refreshToken无效或已过期", codeErr.Error())
  128. }
  129. // TC-0028: 用户已删除(UserDetailsLoader 返回 Status=0 → 403 账号已被冻结)
  130. func TestRefreshToken_UserDeleted(t *testing.T) {
  131. ctx := context.Background()
  132. svcCtx := newTestSvcCtx()
  133. nonExistentUserId := int64(999999999)
  134. refreshToken, err := authHelper.GenerateRefreshToken(
  135. svcCtx.Config.Auth.RefreshSecret,
  136. svcCtx.Config.Auth.RefreshExpire,
  137. nonExistentUserId, "", 0,
  138. )
  139. require.NoError(t, err)
  140. logic := NewRefreshTokenLogic(ctx, svcCtx)
  141. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  142. Authorization: "Bearer " + refreshToken,
  143. })
  144. require.Nil(t, resp)
  145. require.Error(t, err)
  146. var codeErr *response.CodeError
  147. require.True(t, errors.As(err, &codeErr))
  148. assert.Equal(t, 403, codeErr.Code())
  149. assert.Equal(t, "账号已被冻结", codeErr.Error())
  150. }
  151. // TC-0029: 账号冻结
  152. func TestRefreshToken_AccountFrozen(t *testing.T) {
  153. ctx := context.Background()
  154. svcCtx := newTestSvcCtx()
  155. username := testutil.UniqueId()
  156. password := "TestPass123"
  157. userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 2, 2)
  158. t.Cleanup(cleanUser)
  159. refreshToken, err := authHelper.GenerateRefreshToken(
  160. svcCtx.Config.Auth.RefreshSecret,
  161. svcCtx.Config.Auth.RefreshExpire,
  162. userId, "", 0,
  163. )
  164. require.NoError(t, err)
  165. logic := NewRefreshTokenLogic(ctx, svcCtx)
  166. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  167. Authorization: "Bearer " + refreshToken,
  168. })
  169. require.Nil(t, resp)
  170. require.Error(t, err)
  171. var codeErr *response.CodeError
  172. require.True(t, errors.As(err, &codeErr))
  173. assert.Equal(t, 403, codeErr.Code())
  174. assert.Equal(t, "账号已被冻结", codeErr.Error())
  175. }
  176. // TC-0031: 尝试切换产品被拒绝
  177. func TestRefreshToken_ProductCodeSwitchRejected(t *testing.T) {
  178. ctx := context.Background()
  179. svcCtx := newTestSvcCtx()
  180. username := testutil.UniqueId()
  181. password := "TestPass123"
  182. userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
  183. t.Cleanup(cleanUser)
  184. refreshToken, err := authHelper.GenerateRefreshToken(
  185. svcCtx.Config.Auth.RefreshSecret,
  186. svcCtx.Config.Auth.RefreshExpire,
  187. userId, "product_a", 0,
  188. )
  189. require.NoError(t, err)
  190. logic := NewRefreshTokenLogic(ctx, svcCtx)
  191. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  192. Authorization: "Bearer " + refreshToken,
  193. ProductCode: "product_b",
  194. })
  195. require.Nil(t, resp)
  196. require.Error(t, err)
  197. var codeErr *response.CodeError
  198. require.True(t, errors.As(err, &codeErr))
  199. assert.Equal(t, 400, codeErr.Code())
  200. assert.Equal(t, "刷新令牌不允许切换产品", codeErr.Error())
  201. }
  202. // TC-0030: 超管+productCode(refreshToken原样返回)
  203. func TestRefreshToken_SuperAdminWithProductCode(t *testing.T) {
  204. ctx := context.Background()
  205. svcCtx := newTestSvcCtx()
  206. conn := testutil.GetTestSqlConn()
  207. username := testutil.UniqueId()
  208. password := "TestPass123"
  209. pc := testutil.UniqueId()
  210. now := time.Now().Unix()
  211. userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 1)
  212. t.Cleanup(cleanUser)
  213. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  214. t.Cleanup(cleanProduct)
  215. permCode := testutil.UniqueId()
  216. permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  217. ProductCode: pc, Name: "sa_refresh_perm", Code: permCode, Status: 1, CreateTime: now, UpdateTime: now,
  218. })
  219. require.NoError(t, err)
  220. permId, _ := permRes.LastInsertId()
  221. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
  222. refreshToken, err := authHelper.GenerateRefreshToken(
  223. svcCtx.Config.Auth.RefreshSecret,
  224. svcCtx.Config.Auth.RefreshExpire,
  225. userId, pc, 0,
  226. )
  227. require.NoError(t, err)
  228. logic := NewRefreshTokenLogic(ctx, svcCtx)
  229. resp, err := logic.RefreshToken(&types.RefreshTokenReq{
  230. Authorization: "Bearer " + refreshToken,
  231. ProductCode: pc,
  232. })
  233. require.NoError(t, err)
  234. require.NotNil(t, resp)
  235. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回,不重新生成")
  236. assert.Equal(t, "SUPER_ADMIN", resp.UserInfo.MemberType)
  237. assert.Contains(t, resp.UserInfo.Perms, permCode)
  238. assert.Equal(t, int64(1), resp.UserInfo.IsSuperAdmin)
  239. }