You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.6 KiB

3 years ago
  1. package middleware
  2. import (
  3. "context"
  4. "errors"
  5. "net/http"
  6. "time"
  7. "go.uber.org/zap"
  8. "github.com/flipped-aurora/gin-vue-admin/server/global"
  9. "github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
  10. "github.com/gin-gonic/gin"
  11. )
  12. type LimitConfig struct {
  13. // GenerationKey 根据业务生成key 下面CheckOrMark查询生成
  14. GenerationKey func(c *gin.Context) string
  15. // 检查函数,用户可修改具体逻辑,更加灵活
  16. CheckOrMark func(key string, Expire int) bool
  17. // Expire key 过期时间
  18. Expire int
  19. // Limit 周期时间
  20. Limit int
  21. }
  22. func (l *LimitConfig) LimitWithTime() gin.HandlerFunc {
  23. return func(c *gin.Context) {
  24. if l.CheckOrMark(l.GenerationKey(c), l.Expire) {
  25. c.Next()
  26. } else {
  27. c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": "操作频繁,请稍后再试"})
  28. c.Abort()
  29. return
  30. }
  31. }
  32. }
  33. // DefaultGenerationKey 默认生成key
  34. func DefaultGenerationKey(c *gin.Context) string {
  35. return "GVA_Limit" + c.ClientIP()
  36. }
  37. func DefaultCheckOrMark(key string, expire int, limit int) bool {
  38. // 判断是否开启redis
  39. if global.GVA_REDIS == nil {
  40. return true
  41. }
  42. if err := SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil {
  43. global.GVA_LOG.Error("limit", zap.Error(err))
  44. return false
  45. }
  46. return true
  47. }
  48. // IPLimit ip限制
  49. func IPLimit() gin.HandlerFunc {
  50. return func(c *gin.Context) {
  51. key := "RequestClientIPLimit===" + c.ClientIP()
  52. limit := global.GVA_CONFIG.System.LimitCountIP
  53. second := global.GVA_CONFIG.System.LimitTimeIP
  54. expiration := time.Duration(second) * time.Second
  55. if err := SetLimitWithTime(key, limit, expiration); err != nil {
  56. response.FailWithMessage(err.Error(), c)
  57. c.Abort()
  58. return
  59. }
  60. // 继续往下处理
  61. c.Next()
  62. }
  63. }
  64. // 设置访问次数
  65. func SetLimitWithTime(key string, limit int, expiration time.Duration) error {
  66. count, err := global.GVA_REDIS.Exists(context.Background(), key).Result()
  67. if err != nil {
  68. return err
  69. }
  70. if count == 0 {
  71. pipe := global.GVA_REDIS.TxPipeline()
  72. pipe.Incr(context.Background(), key)
  73. pipe.Expire(context.Background(), key, expiration)
  74. _, err := pipe.Exec(context.Background())
  75. return err
  76. } else {
  77. //次数
  78. if times, err := global.GVA_REDIS.Get(context.Background(), key).Int(); err != nil {
  79. return err
  80. } else {
  81. if times >= limit {
  82. if t, err := global.GVA_REDIS.PTTL(context.Background(), key).Result(); err != nil {
  83. return errors.New("请求太过频繁,请稍后再试")
  84. } else {
  85. return errors.New("请求太过频繁, 请 " + t.String() + " 秒后尝试")
  86. }
  87. } else {
  88. return global.GVA_REDIS.Incr(context.Background(), key).Err()
  89. }
  90. }
  91. }
  92. }