RateLimitConfigValidator.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. namespace App\Module\OpenAPI\Validators;
  3. use UCore\Validator;
  4. /**
  5. * 频率限制配置验证器
  6. */
  7. class RateLimitConfigValidator extends Validator
  8. {
  9. /**
  10. * 验证频率限制配置是否有效
  11. *
  12. * @param mixed $value 频率限制配置数组
  13. * @param array $data 验证数据
  14. * @return bool 验证是否通过
  15. */
  16. public function validate(mixed $value, array $data): bool
  17. {
  18. // 从 args 获取参数
  19. $processedKey = $this->args[0] ?? 'processedRateLimits';
  20. if (empty($value)) {
  21. // 使用默认配置
  22. $value = $this->getDefaultRateLimits();
  23. }
  24. if (!is_array($value)) {
  25. $this->addError('频率限制配置必须是数组格式');
  26. return false;
  27. }
  28. $processedLimits = [];
  29. $validTypes = $this->getValidLimitTypes();
  30. foreach ($value as $type => $limit) {
  31. // 验证限制类型
  32. if (!in_array($type, $validTypes)) {
  33. $this->addError("无效的限制类型: {$type}");
  34. return false;
  35. }
  36. // 验证限制值
  37. if (!$this->validateLimitValue($type, $limit)) {
  38. return false;
  39. }
  40. $processedLimits[$type] = (int)$limit;
  41. }
  42. // 验证限制值的合理性
  43. if (!$this->validateLimitReasonableness($processedLimits)) {
  44. return false;
  45. }
  46. // 将处理后的配置保存到验证对象中
  47. $this->validation->$processedKey = $processedLimits;
  48. return true;
  49. }
  50. /**
  51. * 获取有效的限制类型
  52. *
  53. * @return array
  54. */
  55. protected function getValidLimitTypes(): array
  56. {
  57. return [
  58. 'requests_per_minute',
  59. 'requests_per_hour',
  60. 'requests_per_day',
  61. 'requests_per_week',
  62. 'requests_per_month'
  63. ];
  64. }
  65. /**
  66. * 获取默认频率限制配置
  67. *
  68. * @return array
  69. */
  70. protected function getDefaultRateLimits(): array
  71. {
  72. return [
  73. 'requests_per_minute' => 60,
  74. 'requests_per_hour' => 1000,
  75. 'requests_per_day' => 10000,
  76. 'requests_per_week' => 50000,
  77. 'requests_per_month' => 200000,
  78. ];
  79. }
  80. /**
  81. * 验证限制值
  82. *
  83. * @param string $type
  84. * @param mixed $limit
  85. * @return bool
  86. */
  87. protected function validateLimitValue(string $type, mixed $limit): bool
  88. {
  89. if (!is_numeric($limit)) {
  90. $this->addError("限制值必须是数字: {$type}");
  91. return false;
  92. }
  93. $limit = (int)$limit;
  94. if ($limit < 0) {
  95. $this->addError("限制值不能为负数: {$type}");
  96. return false;
  97. }
  98. // 验证最大限制值
  99. $maxLimits = [
  100. 'requests_per_minute' => 10000,
  101. 'requests_per_hour' => 100000,
  102. 'requests_per_day' => 1000000,
  103. 'requests_per_week' => 5000000,
  104. 'requests_per_month' => 20000000,
  105. ];
  106. if (isset($maxLimits[$type]) && $limit > $maxLimits[$type]) {
  107. $this->addError("限制值过大: {$type} 最大值为 {$maxLimits[$type]}");
  108. return false;
  109. }
  110. return true;
  111. }
  112. /**
  113. * 验证限制值的合理性
  114. *
  115. * @param array $limits
  116. * @return bool
  117. */
  118. protected function validateLimitReasonableness(array $limits): bool
  119. {
  120. // 验证时间单位之间的合理性
  121. $timeUnits = [
  122. 'requests_per_minute' => 1,
  123. 'requests_per_hour' => 60,
  124. 'requests_per_day' => 1440,
  125. 'requests_per_week' => 10080,
  126. 'requests_per_month' => 43200, // 30天
  127. ];
  128. foreach ($limits as $type => $limit) {
  129. if (!isset($timeUnits[$type])) {
  130. continue;
  131. }
  132. $perMinute = $limit / $timeUnits[$type];
  133. // 检查是否有更短时间单位的限制
  134. foreach ($timeUnits as $shorterType => $shorterMultiplier) {
  135. if ($shorterMultiplier >= $timeUnits[$type]) {
  136. continue;
  137. }
  138. if (isset($limits[$shorterType])) {
  139. $shorterPerMinute = $limits[$shorterType] / $shorterMultiplier;
  140. if ($perMinute > $shorterPerMinute) {
  141. $this->addError("限制配置不合理: {$type} 的平均速率不能超过 {$shorterType}");
  142. return false;
  143. }
  144. }
  145. }
  146. }
  147. return true;
  148. }
  149. }