authutil.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. package authutil
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/go-micro/plugins/v4/auth/jwt"
  7. "github.com/go-redsync/redsync/v4"
  8. "github.com/go-redsync/redsync/v4/redis/goredis/v9"
  9. "github.com/google/uuid"
  10. goredislib "github.com/redis/go-redis/v9"
  11. "github.com/sirupsen/logrus"
  12. "go-micro.dev/v4/auth"
  13. "os"
  14. "os/signal"
  15. "sghgogs.com/micro/common"
  16. "sghgogs.com/micro/common/errorcode"
  17. "strconv"
  18. "strings"
  19. "sync"
  20. "syscall"
  21. "time"
  22. )
  23. var (
  24. JWTAuthService *JWTAuth
  25. jwtAuthServiceOnce sync.Once
  26. )
  27. const (
  28. expiry = time.Second * time.Duration(0.5*3600)
  29. // expiry = time.Second * time.Duration(1*3600)
  30. )
  31. type JWTAuth struct {
  32. Mu *redsync.Redsync
  33. Client *goredislib.Client
  34. Auth auth.Auth
  35. namespace string
  36. Enable bool
  37. RulesItems []*auth.Rule
  38. }
  39. type TeamTokenData struct {
  40. Token string `json:"token"`
  41. ExpirationTime int64 `json:"expiration_time"`
  42. }
  43. func NewJWTAuth(client *goredislib.Client, namespace string, enable bool) *JWTAuth {
  44. jwtAuthServiceOnce.Do(func() {
  45. newAuth := jwt.NewAuth(
  46. auth.Namespace(namespace),
  47. auth.PrivateKey("LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS3dJQkFBS0NBZ0VBOFNiSlA1WGJFaWRSbTViMnNOcExHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkCi9SbDkvMXBNVjdNaU8zTEh3dGhIQzJCUllxcisxd0Zkb1pDR0JZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUKMEJIL2xYUU1xeUVxRjVNSTJ6ZWpDNHpNenIxNU9OK2dFNEpuaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtLwptVWRJVC9MYUY3a1F4eVlLNVZLbitOZ09Xek1sektBQXBDbjdUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsCm85akRqbFk1b0JPY3pmcWVOV0hLNUdYQjdRd3BMTmg5NDZQelpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDUKd2xFcThoTmhtaG01Tk5lL08rR2dqQkROU2ZVaDA2K3E0bmdtYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1bwpSdFFoZ2lZOTEwcFBmOWJhdVhXcXdVQ1VhNHFzSHpqS1IwTC9OMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVCnJnTHJQYkVCOWVnY0drMzgrYnBLczNaNlJyNSt0bkQxQklQSUZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVUKVEdEeFV4OG9qOFZJZVJuV0RxNk1jMWlKcDhVeWNpQklUUnR3NGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMApsYVF6QXVQM2FpV1hJTXAyc2M4U2MrQmwrTGpYbUJveEJyYUJIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YCmdGS1NzSW5IRHJIVk95V1BCZTNmYWRFYzc3YituYi9leE96cjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUEKQVFLQ0FnRUFqUzc1Q2VvUlRRcUtBNzZaaFNiNGEzNVlKRENtcEpSazFsRTNKYnFzNFYxRnhXaDBjZmJYeG9VMgpSdTRRYjUrZWhsdWJGSFQ2a1BxdG9uRWhRVExjMUNmVE9WbHJOb3hocDVZM2ZyUmlQcnNnNXcwK1R3RUtrcFJUCnltanJQTXdQbGxCM2U0NmVaYmVXWGc3R3FFVmptMGcxVFRRK0tocVM4R0w3VGJlTFhRN1ZTem9ydTNCNVRKMVEKeEN6TVB0dnQ2eDYrU3JrcmhvZG1iT3VNRkpDam1TbWxmck9pZzQ4Zkc3NUpERHRObXpLWHBEUVJpYUNodFJhVQpQRHpmUTlTamhYdFFqdkZvWFFFT3BqdkZVRjR2WldNUWNQNUw1VklDM3JRSWp4MFNzQTN6S0FwakVUbjJHNjN2CktZby8zVWttbzhkUCtGRHA3NCs5a3pLNHFFaFJycEl3bEtiN0VOZWtDUXZqUFl1K3pyKzMyUXdQNTJ2L2FveWQKdjJJaUY3M2laTU1vZDhhYjJuQStyVEI2T0cvOVlSYk5kV21tay9VTi9jUHYrN214TmZ6Y1d1ZU1XcThxMXh4eAptNTNpR0NSQ29PQ1lDQk4zcUFkb1JwYW5xd3lCOUxrLzFCQjBHUld3MjgxK3VhNXNYRnZBVDBKeTVURnduMncvClU1MlJKWFlNOXVhMFBvd214b0RDUWRuNFZYVkdNZGdXaHN4aXhHRlYwOUZObWJJQWJaN0xaWGtkS1gzc1ZVbTcKWU1WYWIzVVo2bEhtdXYzT1NzcHNVUlRqN1hiRzZpaVVlaDU1aW91OENWbnRndWtFcnEzQTQwT05FVzhjNDBzOQphVTBGaSs4eWZpQTViaVZHLzF0bWlucUVERkhuQStnWk1xNEhlSkZxcWZxaEZKa1JwRGtDZ2dFQkFQeGR1NGNKCm5Da1duZDdPWFlHMVM3UDdkVWhRUzgwSDlteW9uZFc5bGFCQm84RWRPeTVTZzNOUmsxQ2pNZFZ1a3FMcjhJSnkKeStLWk15SVpvSlJvbllaMEtIUUVMR3ZLbzFOS2NLQ1FJbnYvWHVCdFJpRzBVb1pQNVkwN0RpRFBRQWpYUjlXUwpBc0EzMmQ1eEtFOC91Y3h0MjVQVzJFakNBUmtVeHQ5d0tKazN3bC9JdXVYRlExTDdDWjJsOVlFUjlHeWxUbzhNCmxXUEY3YndtUFV4UVNKaTNVS0FjTzZweTVUU1lkdWQ2aGpQeXJwSXByNU42VGpmTlRFWkVBeU9LbXVpOHVkUkoKMUg3T3RQVEhGZElKQjNrNEJnRDZtRE1HbjB2SXBLaDhZN3NtRUZBbFkvaXlCZjMvOHk5VHVMb1BycEdqR3RHbgp4Y2RpMHFud2p0SGFNbFVDZ2dFQkFQU2Z0dVFCQ2dTU2JLUSswUEFSR2VVeEQyTmlvZk1teENNTmdHUzJ5Ull3CjRGaGV4ZWkwMVJoaFk1NjE3UjduR1dzb0czd1RQa3dvRTJtbE1aQkoxeWEvUU9RRnQ3WG02OVl0RGh0T2FWbDgKL0o4dlVuSTBtWmxtT2pjTlRoYnVPZDlNSDlRdGxIRUMxMlhYdHJNb3Fsb0U2a05TT0pJalNxYm9wcDRXc1BqcApvZTZ0Nkdyd1RhOHBHeUJWWS90Mi85Ym5ORHVPVlpjODBaODdtY2gzcDNQclBqU3h5di9saGxYMFMwYUdHTkhTCk1XVjdUa25OaGo1TWlIRXFnZ1pZemtBWTkyd1JoVENnU1A2M0VNcitUWXFudXVuMXJHbndPYm95TDR2aFRpV0UKcU42UDNCTFlCZ1FpMllDTDludEJrOEl6RHZyd096dW5GVnhhZ0g5SVVoY0NnZ0VCQUwzQXlLa1BlOENWUmR6cQpzL284VkJDZmFSOFhhUGRnSGxTek1BSXZpNXEwNENqckRyMlV3MHZwTVdnM1hOZ0xUT3g5bFJpd3NrYk9SRmxHCmhhd3hRUWlBdkk0SE9WTlBTU0R1WHVNTG5USTQ0S0RFNlMrY2cxU0VMS2pWbDVqcDNFOEpkL1RJMVpLc0xBQUsKZTNHakM5UC9ZbE8xL21ndW4xNjVkWk01cFAwWHBPb2FaeFV2RHFFTktyekR0V1g0RngyOTZlUzdaSFJodFpCNwovQ2t1VUhlcmxrN2RDNnZzdWhTaTh2eTM3c0tPbmQ0K3c4cVM4czhZYVZxSDl3ZzVScUxxakp0bmJBUnc3alVDCm9KQ053M1hNdnc3clhaYzRTbnhVQUNMRGJNV2lLQy9xL1ZGWW9oTEs2WkpUVkJscWd5cjBSYzBRWmpDMlNJb0kKMjRwRWt3VUNnZ0VCQUpqb0FJVVNsVFY0WlVwaExXN3g4WkxPa01UWjBVdFFyd2NPR0hSYndPUUxGeUNGMVFWNQppejNiR2s4SmZyZHpVdk1sTmREZm9uQXVHTHhQa3VTVEUxWlg4L0xVRkJveXhyV3dvZ0cxaUtwME11QTV6em90CjROai9DbUtCQVkvWnh2anA5M2RFS21aZGxWQkdmeUFMeWpmTW5MWUovZXh5L09YSnhPUktZTUttSHg4M08zRWsKMWhvb0FwbTZabTIzMjRGME1iVU1ham5Idld2ZjhHZGJTNk5zcHd4L0dkbk1tYVMrdUJMVUhVMkNLbmc1bEIwVAp4OWJITmY0dXlPbTR0dXRmNzhCd1R5V3UreEdrVW0zZ2VZMnkvR1hqdDZyY2l1ajFGNzFDenZzcXFmZThTcDdJCnd6SHdxcTNzVHR5S2lCYTZuYUdEYWpNR1pKYSt4MVZJV204Q2dnRUJBT001ajFZR25Ba0pxR0czQWJSVDIvNUMKaVVxN0loYkswOGZsSGs5a2YwUlVjZWc0ZVlKY3dIRXJVaE4rdWQyLzE3MC81dDYra0JUdTVZOUg3bkpLREtESQpoeEg5SStyamNlVkR0RVNTRkluSXdDQ1lrOHhOUzZ0cHZMV1U5b0pibGFKMlZsalV2NGRFWGVQb0hkREh1Zk9ZClVLa0lsV2E3Uit1QzNEOHF5U1JrQnFLa3ZXZ1RxcFNmTVNkc1ZTeFIzU2Q4SVhFSHFjTDNUNEtMWGtYNEdEamYKMmZOSTFpZkx6ekhJMTN3Tk5IUTVRNU9SUC9pell2QzVzZkx4U2ZIUXJiMXJZVkpKWkI5ZjVBUjRmWFpHSVFsbApjMG8xd0JmZFlqMnZxVDlpR09IQnNSSTlSL2M2RzJQcUt3aFRpSzJVR2lmVFNEUVFuUkF6b2tpQVkrbE8vUjQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg=="),
  48. auth.PublicKey("LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE4U2JKUDVYYkVpZFJtNWIyc05wTApHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkL1JsOS8xcE1WN01pTzNMSHd0aEhDMkJSWXFyKzF3RmRvWkNHCkJZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUwQkgvbFhRTXF5RXFGNU1JMnplakM0ek16cjE1T04rZ0U0Sm4KaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtL21VZElUL0xhRjdrUXh5WUs1VktuK05nT1d6TWx6S0FBcENuNwpUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsbzlqRGpsWTVvQk9jemZxZU5XSEs1R1hCN1F3cExOaDk0NlB6ClpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDV3bEVxOGhOaG1obTVOTmUvTytHZ2pCRE5TZlVoMDYrcTRuZ20KYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1b1J0UWhnaVk5MTBwUGY5YmF1WFdxd1VDVWE0cXNIempLUjBMLwpOMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVcmdMclBiRUI5ZWdjR2szOCticEtzM1o2UnI1K3RuRDFCSVBJCkZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVVUR0R4VXg4b2o4VkllUm5XRHE2TWMxaUpwOFV5Y2lCSVRSdHcKNGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMGxhUXpBdVAzYWlXWElNcDJzYzhTYytCbCtMalhtQm94QnJhQgpIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YZ0ZLU3NJbkhEckhWT3lXUEJlM2ZhZEVjNzdiK25iL2V4T3pyCjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="),
  49. )
  50. pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)
  51. // Create an instance of redisync to be used to obtain a mutual exclusion
  52. // lock.
  53. rs := redsync.New(pool)
  54. JWTAuthService = &JWTAuth{
  55. Mu: rs,
  56. Client: client,
  57. Auth: newAuth,
  58. namespace: namespace,
  59. Enable: enable,
  60. }
  61. handleSignals(client)
  62. })
  63. return JWTAuthService
  64. }
  65. func (svc *JWTAuth) SetRuleItems(rules []*auth.Rule) error {
  66. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-rule-item-lock", svc.namespace))
  67. // 获取锁,保证原子性
  68. if err := lock.Lock(); err != nil {
  69. return err
  70. }
  71. defer lock.Unlock()
  72. // Marshal the data structure to JSON
  73. jsonData, _ := json.Marshal(rules)
  74. ctx := context.Background()
  75. key := fmt.Sprintf("%v_rules_hash", svc.namespace)
  76. value := fmt.Sprintf("%v_rules_key", svc.namespace)
  77. svc.Client.HSet(ctx, key, value, jsonData)
  78. return nil
  79. }
  80. func (svc *JWTAuth) GetRuleItems() []*auth.Rule {
  81. roles := make([]*auth.Rule, 0)
  82. ctx := context.Background()
  83. key := fmt.Sprintf("%v_rules_hash", svc.namespace)
  84. value := fmt.Sprintf("%v_rules_key", svc.namespace)
  85. result, err := svc.Client.HGet(ctx, key, value).Result()
  86. if err != nil {
  87. return roles
  88. }
  89. var retrievedItems []*auth.Rule
  90. err = json.Unmarshal([]byte(result), &retrievedItems)
  91. if err != nil {
  92. fmt.Println("Error unmarshaling data:", err)
  93. return roles
  94. }
  95. return retrievedItems
  96. }
  97. // GenerateToken 示例:生成令牌
  98. func (svc *JWTAuth) GenerateToken(userID int64, provider, withType, secret string, scopes []string, md map[string]string) (*auth.Account, error) {
  99. var account *auth.Account
  100. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-generate-token-lock-%d", svc.namespace, userID))
  101. // 获取锁,保证原子性
  102. if err := lock.Lock(); err != nil {
  103. return account, err
  104. }
  105. defer lock.Unlock()
  106. generate, err := svc.Auth.Generate(
  107. fmt.Sprintf("%d", userID),
  108. auth.WithType(withType),
  109. auth.WithProvider(provider),
  110. auth.WithScopes(strings.Join(scopes, ",")),
  111. auth.WithSecret(secret),
  112. auth.WithMetadata(md),
  113. )
  114. if err != nil {
  115. code := common.FailedToGenerateTokenErrorCode
  116. return account, errorcode.New(svc.namespace, common.ErrorMessage[code], int32(code))
  117. }
  118. return generate, nil
  119. }
  120. // Token 重新刷新token
  121. func (svc *JWTAuth) Token(userID int64, accessToken string, expiry time.Duration) (*auth.Token, error) {
  122. var authToken *auth.Token
  123. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-token-lock-%d", svc.namespace, userID))
  124. // 获取锁,保证原子性
  125. if err := lock.Lock(); err != nil {
  126. return authToken, err
  127. }
  128. defer lock.Unlock()
  129. return svc.Auth.Token(
  130. auth.WithExpiry(expiry),
  131. auth.WithCredentials(fmt.Sprintf("%d", userID), accessToken),
  132. auth.WithToken(accessToken),
  133. )
  134. }
  135. // Inspect 检测token 有效期
  136. func (svc *JWTAuth) Inspect(accessToken string) (*auth.Account, error) {
  137. var account *auth.Account
  138. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-inspect-lock-%d", svc.namespace, uuid.New()))
  139. // 获取锁,保证原子性
  140. if err := lock.Lock(); err != nil {
  141. return account, err
  142. }
  143. defer lock.Unlock()
  144. return svc.Auth.Inspect(accessToken)
  145. }
  146. func (svc *JWTAuth) RefreshToken(token string) (*auth.Token, error) {
  147. var authToken *auth.Token
  148. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-refresh-generate-token-lock", svc.namespace))
  149. // 获取锁,保证原子性
  150. if err := lock.Lock(); err != nil {
  151. return authToken, err
  152. }
  153. defer lock.Unlock()
  154. inspect, err := svc.Auth.Inspect(token)
  155. if err != nil {
  156. return authToken, err
  157. }
  158. // 重新设置过期时间
  159. var expiryTime time.Duration
  160. if value, ok := inspect.Metadata["timestamp"]; ok {
  161. timestamp, _ := strconv.ParseInt(value, 10, 64)
  162. expiryTime = time.Second * time.Duration(timestamp)
  163. } else {
  164. expiryTime = expiry
  165. }
  166. inspect.Metadata["expiry"] = fmt.Sprintf("%d", time.Now().Add(expiryTime).Unix())
  167. generate, err := svc.Auth.Generate(
  168. inspect.Metadata["id"],
  169. auth.WithType("user"),
  170. auth.WithProvider("system"),
  171. auth.WithScopes(strings.Join(inspect.Scopes, ",")),
  172. auth.WithSecret(inspect.Metadata["password"]),
  173. auth.WithMetadata(inspect.Metadata),
  174. )
  175. if err != nil {
  176. code := common.FailedToGenerateTokenErrorCode
  177. return authToken, errorcode.New(svc.namespace, common.ErrorMessage[code], int32(code))
  178. }
  179. userId, _ := strconv.ParseInt(inspect.Metadata["id"], 10, 64)
  180. return svc.Token(userId, generate.Secret, expiryTime)
  181. }
  182. func (svc *JWTAuth) GetTeamFromRedis(ctx context.Context, name string) (*TeamTokenData, error) {
  183. tokenDataJSON, err := svc.Client.Get(ctx, fmt.Sprintf("team:%v:token", name)).Result()
  184. if err == goredislib.Nil {
  185. // 缓存中没有数据
  186. return nil, nil
  187. } else if err != nil {
  188. return nil, err
  189. }
  190. // 解析 JSON 数据为 TokenData 结构
  191. var tokenData TeamTokenData
  192. err = json.Unmarshal([]byte(tokenDataJSON), &tokenData)
  193. if err != nil {
  194. return nil, err
  195. }
  196. return &tokenData, nil
  197. }
  198. func (svc *JWTAuth) SetToTeamRedis(ctx context.Context, name string, tokenData *TeamTokenData) error {
  199. tokenDataJSON, err := json.Marshal(tokenData)
  200. if err != nil {
  201. return err
  202. }
  203. expirationTime := time.Unix(tokenData.ExpirationTime, 0).Sub(time.Now())
  204. // 设置 Redis 缓存,有效期为计算出的过期时间
  205. err = svc.Client.Set(ctx, fmt.Sprintf("team:%v:token", name), tokenDataJSON, expirationTime).Err()
  206. if err != nil {
  207. return err
  208. }
  209. return nil
  210. }
  211. // StoreTeamRevoke 中删除令牌
  212. func (svc *JWTAuth) StoreTeamRevoke(ctx context.Context, name string) error {
  213. lock := svc.Mu.NewMutex(fmt.Sprintf("store-revoke-lock-%v", name))
  214. // 获取锁
  215. if err := lock.Lock(); err != nil {
  216. return err
  217. }
  218. defer lock.Unlock()
  219. access := fmt.Sprintf(fmt.Sprintf("team:%v:token", name))
  220. err := svc.Client.Del(ctx, access).Err()
  221. if err != nil {
  222. err = errorcode.New(svc.namespace, common.ErrorMessage[common.TokenDeletionFailedErrorCode], int32(common.TokenDeletionFailedErrorCode))
  223. logrus.Error(err)
  224. return err
  225. }
  226. return nil
  227. }
  228. // StoreToken 存储令牌到Redis
  229. func (svc *JWTAuth) StoreToken(ctx context.Context, ID int64, name, accessToken string) error {
  230. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-store-token-lock-%d", svc.namespace, ID))
  231. // 获取锁
  232. if err := lock.Lock(); err != nil {
  233. return err
  234. }
  235. access := fmt.Sprintf("auth:%v-access-token:%v:%s", svc.namespace, ID, name)
  236. err := svc.Client.Set(ctx, access, accessToken, expiry).Err()
  237. if err != nil {
  238. err = errorcode.New(svc.namespace, common.ErrorMessage[common.FailedToStoreTokenErrorCode], int32(common.FailedToStoreTokenErrorCode))
  239. logrus.Error(err)
  240. return err
  241. }
  242. return nil
  243. }
  244. // StoreRevoke 从Redis中删除令牌
  245. func (svc *JWTAuth) StoreRevoke(ctx context.Context, ID int64, name string) error {
  246. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-store-revoke-lock-%d", svc.namespace, ID))
  247. // 获取锁
  248. if err := lock.Lock(); err != nil {
  249. return err
  250. }
  251. defer lock.Unlock()
  252. access := fmt.Sprintf("auth:%v-access-token:%v:%s", svc.namespace, ID, name)
  253. err := svc.Client.Del(ctx, access).Err()
  254. if err != nil {
  255. err = errorcode.New(svc.namespace, common.ErrorMessage[common.TokenDeletionFailedErrorCode], int32(common.TokenDeletionFailedErrorCode))
  256. logrus.Error(err)
  257. return err
  258. }
  259. return nil
  260. }
  261. // StoreVerify 检查是否在有效期内
  262. func (svc *JWTAuth) StoreVerify(ID int64, name string, accessToken string) bool {
  263. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-store-verify-lock-%d", svc.namespace, ID))
  264. // 获取锁
  265. if err := lock.Lock(); err != nil {
  266. // 处理获取锁失败的情况
  267. return false
  268. }
  269. defer lock.Unlock()
  270. token := fmt.Sprintf("auth:%v-access-token:%v:%s", svc.namespace, ID, name)
  271. return svc.Client.Get(context.Background(), token).Val() == accessToken
  272. }
  273. // Blacklist 将令牌添加到 Redis 黑名单并设置过期时间。
  274. func (svc *JWTAuth) Blacklist(accessToken string) error {
  275. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-blacklist-lock-%d", svc.namespace, uuid.New()))
  276. // 获取锁
  277. if err := lock.Lock(); err != nil {
  278. return err
  279. }
  280. defer lock.Unlock()
  281. // 将令牌添加到 Redis,并设置过期时间
  282. return svc.Client.Set(context.Background(), accessToken, fmt.Sprintf("%v-blacklisted", svc.namespace), expiry).Err()
  283. }
  284. // IsBlacklisted 检查令牌是否在黑名单中。
  285. func (svc *JWTAuth) IsBlacklisted(accessToken string) (bool, error) {
  286. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-is-blacklisted-lock-%d", svc.namespace, uuid.New()))
  287. // 获取锁
  288. if err := lock.Lock(); err != nil {
  289. // 处理获取锁失败的情况
  290. return false, err
  291. }
  292. defer lock.Unlock()
  293. // 检查令牌是否存在于 Redis 中
  294. _, err := svc.Client.Get(context.Background(), accessToken).Result()
  295. if err == goredislib.Nil {
  296. return false, nil
  297. } else if err != nil {
  298. return false, err
  299. }
  300. return true, nil
  301. }
  302. // CleanupExpiredTokens 定期清理 Redis 黑名单中的过期令牌。
  303. func (svc *JWTAuth) CleanupExpiredTokens(interval time.Duration) {
  304. ticker := time.NewTicker(interval)
  305. defer ticker.Stop()
  306. for {
  307. select {
  308. case <-ticker.C:
  309. svc.cleanupExpiredTokens()
  310. }
  311. }
  312. }
  313. func (svc *JWTAuth) cleanupExpiredTokens() {
  314. lock := svc.Mu.NewMutex(fmt.Sprintf("%v-cleanup-expired-tokens-lock", svc.namespace))
  315. // 获取锁
  316. if err := lock.Lock(); err != nil {
  317. // 处理获取锁失败的情况
  318. return
  319. }
  320. defer lock.Unlock()
  321. // 遍历并从 Redis 中删除过期的键
  322. iter := svc.Client.Scan(context.Background(), 0, "*", 0).Iterator()
  323. for iter.Next(context.Background()) {
  324. key := iter.Val()
  325. ttl := svc.Client.TTL(context.Background(), key).Val()
  326. if ttl.Seconds() <= 0 {
  327. svc.Client.Del(context.Background(), key)
  328. }
  329. }
  330. }
  331. // handleSignals 在应用程序退出时处理信号
  332. func handleSignals(client *goredislib.Client) {
  333. c := make(chan os.Signal, 1)
  334. signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
  335. go func() {
  336. <-c
  337. fmt.Println("Closing Redis connection...")
  338. if err := client.Close(); err != nil {
  339. fmt.Println("Error closing Redis connection:", err)
  340. } else {
  341. fmt.Println("Redis connection closed.")
  342. }
  343. os.Exit(0)
  344. }()
  345. }