• laravel 自定义节流中间件


    我以前说过,现在再说一遍,使用框架的最大好处之一是许多常见的用例或问题已经得到解决——限制请求也不例外 。

    开箱即用,Laravel cms附带一个ThrottleRequests中间件,该中间件易于配置,可提供 IP 地址在一段时间内可以发出的请求数。

    例如,路由文件中的以下代码片段会将用户或 IP 地址可以发出的请求数限制为每分钟 60 次:

    1. Route::middleware('throttle:rate_limit,60')->group(function () {
    2. Route::get('/products', function () {
    3. //
    4. });
    5. Route::get('/locations', function () {
    6. //
    7. });
    8. });

    您可以将其设置为3,600默认值,然后根据特定用户的需要向上或向下调整。

    但是,如果您想限制一条(或多条)特定路线而不使用这些方法中的任何一种怎么办?

    在最近的一个项目中,用户能够添加 IoT 设备并获得一个唯一的 URL,他们可以将其用作 webhook 以将数据发送到我们的系统。在 URL 中使用唯一键,我需要能够限制他们可能发出的请求。

    一旦你知道如何实现,这实际上很容易实现——我们只需要了解一点现有ThrottleRequests中间件的工作原理。

    如果您打开ThrottleRequests中间件并查看该handle()方法,我们可以看到以下内容:

    1. public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    2. {
    3. $key = $this->resolveRequestSignature($request);
    4. $maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
    5. if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
    6. throw $this->buildException($key, $maxAttempts);
    7. }
    8. $this->limiter->hit($key, $decayMinutes * 60);
    9. $response = $next($request);
    10. return $this->addHeaders(
    11. $response, $maxAttempts,
    12. $this->calculateRemainingAttempts($key, $maxAttempts)
    13. );
    14. }

    该方法的第一行是我们感兴趣的:

    $key = $this->resolveRequestSignature($request);

    这是ThrottleRequests中间件找出应该用来跟踪您的请求的密钥的地方。

    中间件上的方法ThrottleRequests如下所示:

    1. protected function resolveRequestSignature($request)
    2. {
    3. if ($user = $request->user()) {
    4. return sha1($user->getAuthIdentifier());
    5. }
    6. if ($route = $request->route()) {
    7. return sha1($route->getDomain().'|'.$request->ip());
    8. }
    9. throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
    10. }

    如果您是经过身份验证的用户,那么它将使用id您模型的键名(通常除非您已覆盖它)User。否则,它将使用请求的域和 IP 地址作为密钥。

    知道了这一点,我们只需要重写resolveRequestSignature()方法来为请求派生我们自己的密钥:

    app/Http/Middleware在called中添加一个新文件CustomThrottleMiddleware(或者你想称呼这个中间件的任何东西)或者使用 scaffold 一些新的中间件php artisan make:middleware CustomThrottleMiddleware 将内容替换为:

    1. namespace App\Http\Middleware;
    2. use Illuminate\Routing\Middleware\ThrottleRequests;
    3. class CustomThrottleMiddleware extends ThrottleRequests
    4. {
    5. protected function resolveRequestSignature($request)
    6. {
    7. //
    8. }
    9. }

    我们所做的是创建我们自己的新中间件,它扩展了现有的ThrottleRequests中间件,现在设置为覆盖该resolveRequestSignature()方法,以便我们可以设置我们自己的密钥。

    现在你只需要将你的代码添加到resolveRequestSignature()方法中以实际返回一个键——我将在下面包括一些例子:

    1. protected function resolveRequestSignature($request)
    2. {
    3. // Throttle by a particular header
    4. return $request->header('API-Key');
    5. }
    1. protected function resolveRequestSignature($request)
    2. {
    3. // Throttle by a request parameter
    4. return $request->input('account_id');
    5. }
    1. protected function resolveRequestSignature($request)
    2. {
    3. // Throttle by session ID
    4. return $request->session();
    5. }
    1. protected function resolveRequestSignature($request)
    2. {
    3. // Throttle by IP to a particular Model in the route - where `product` is our model and this route is set to use `product` with route model binding
    4. return $request->route('product') . '|' . $request->ip();
    5. }

     在我的用例中,我选择了通过多个参数进行节流。我们为用户提供的唯一 URL 对他们来说是唯一的,但对于所有相同类型的物联网设备都是相同的。例如,我们支持的物联网设备类型A为 、BC;如果您有两种类型,A那么它们共享相同的唯一 URL。我们对此的推理对这篇文章并不重要,所以我将其省略。

    每个设备被允许每 5 分钟发出一个请求,但是由于两个类型A的设备在技术上是两个独立的设备,我们需要包括一种将两者分开的额外方法。

    为实现这一目标,我们只是弄清楚我们使用的是什么类型的物联网设备,然后从那里适当地解析令牌:

    1. protected function resolveRequestSignature($request)
    2. {
    3. $token = $request->route()->parameter('deviceByToken');
    4. $device = IotDevice::findByToken($token)->firstOrFail();
    5. if ($device->type === 'A')
    6. {
    7. return $token . '-' . $request->input('unique_parameter_for_type_a_per_device');
    8. } else if ($device->type === 'B')
    9. {
    10. return $token . '-' . $request->input('unique_parameter_for_type_b_per_device');
    11. } if ($device->type === 'C')
    12. {
    13. return $token . '-' . $request->input('unique_parameter_for_type_c_per_device');
    14. }
    15. return $token;
    16. }

    最后但同样重要的是,您只需app/Http/Kernel.php像这样更新文件来注册中间件以供使用:

     

    1. protected $routeMiddleware = [
    2. //...
    3. 'customthrottle' => \App\Http\Middleware\CustomThrottleMiddleware::class,
    4. ];

    然后,这允许您像这样使用新的中间件(替换 1 和 5 以满足您的需要):

    1. Route::middleware('customthrottle:1,5')->group(function () {
    2. Route::get('/products', function () {
    3. //
    4. });
    5. Route::get('/locations', function () {
    6. //
    7. });
    8. });

    使用上述方法,我们能够使用每个请求中存在的唯一路由参数以及每个设备在请求的有效负载中包含的唯一标识符来限制请求 

  • 相关阅读:
    计算机视觉与模式识别实验1-4 图像的傅立叶变换
    MFC真的过时了吗?C++是否真的适合做GUI界面?
    PDF 工具箱
    ESP-IDF工程自定义组件
    excel转换 txt竖线分隔符 为 xlsx
    淘宝API接口测试需知
    Fiddler 手机抓包代理设置(针对华为荣耀60S)
    VB.NET三层之用户查询窗体
    解密铭控传感从数字压力表到多品类物联感知硬件的发展史!
    如何在Spring Boot中配置MySQL数据库连接数
  • 原文地址:https://blog.csdn.net/lemqs0123/article/details/127955145