Laravel throttle 如何基于路由限流?

Laravel 5.8 利用 throttle 中间件进行限流,发现并不能基于路由进行限流,请问如何解决?

例子:

Route::get('test1', 'DemoController@test1')->middleware('throttle:2,1');
Route::get('test2', 'DemoController@test2')->middleware('throttle:5,1');

经测试,1分钟内3请求 test1 路由,被429限流。再访问 test2 时,也已经被限流了。但 test2 还未达到限流次数5次。请问如何基于路由进行分别限流?

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

– 自己重新写一个基于路由限流的中间件即可
– 把resolveRequestSignature方法重写下即可

<?php

namespace App\Http\Middleware;

use Illuminate\Routing\Middleware\ThrottleRequests as Middleware;
use RuntimeException;
use Closure;

class ThrottleRouteRequests extends Middleware
{
    /**
     * 不限制访问频率的路由
     *
     * @var array
     */
    protected $except = [

    ];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  int|string  $maxAttempts
     * @param  float|int  $decayMinutes
     * @return mixed
     * @throws \Symfony\Component\HttpKernel\Exception\HttpException
     */
    public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        $route = $request->route();
        if (in_array($route->getName(), $this->except)) {
            return $next($request);
        }

        return parent::handle($request, $next, $maxAttempts, $decayMinutes);
    }

    /**
     * Resolve request signature.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     * @throws \RuntimeException
     */
    protected function resolveRequestSignature($request)
    {
        $user = $request->user();
        $route = $request->route();

        //单个路由访问频率限制
        if ($user && $route) {
            return $route->getName().'|'.sha1($user->getAuthIdentifier());
        }

        if ($route) {
            return sha1($route->getName().'|'.$route->getDomain().'|'.$request->ip());
        }

        throw new RuntimeException(
            'Unable to generate the request signature. Route unavailable.'
        );
    }
}
4年前 评论
Mr-houzi (楼主) 4年前
讨论数量: 2

– 自己重新写一个基于路由限流的中间件即可
– 把resolveRequestSignature方法重写下即可

<?php

namespace App\Http\Middleware;

use Illuminate\Routing\Middleware\ThrottleRequests as Middleware;
use RuntimeException;
use Closure;

class ThrottleRouteRequests extends Middleware
{
    /**
     * 不限制访问频率的路由
     *
     * @var array
     */
    protected $except = [

    ];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  int|string  $maxAttempts
     * @param  float|int  $decayMinutes
     * @return mixed
     * @throws \Symfony\Component\HttpKernel\Exception\HttpException
     */
    public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        $route = $request->route();
        if (in_array($route->getName(), $this->except)) {
            return $next($request);
        }

        return parent::handle($request, $next, $maxAttempts, $decayMinutes);
    }

    /**
     * Resolve request signature.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     * @throws \RuntimeException
     */
    protected function resolveRequestSignature($request)
    {
        $user = $request->user();
        $route = $request->route();

        //单个路由访问频率限制
        if ($user && $route) {
            return $route->getName().'|'.sha1($user->getAuthIdentifier());
        }

        if ($route) {
            return sha1($route->getName().'|'.$route->getDomain().'|'.$request->ip());
        }

        throw new RuntimeException(
            'Unable to generate the request signature. Route unavailable.'
        );
    }
}
4年前 评论
Mr-houzi (楼主) 4年前

throttle功能是由 Illuminate\Routing\Middleware\ThrottleRequests类实现的

查看那个类会发现有一个叫fingerprint的函数

是定义Request的规则的地方

把这个部分改了就可以

当然不建议直接改这个文件,因为这个是Laravel vendor文件夹里面的,

建议继承这个类,新造一个中间件类,重写fingerprint函数的相关逻辑,然后把这个中间件注册到需要的router上就行了

4年前 评论
大毛 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!