Laravel 5:如何重置内置油门/速率限制器?

时间:2018-05-02 03:35:57

标签: php laravel laravel-5.5 rate-limiting throttling

我正在使用Laravel的内置油门:

//File: Kernal
protected $middlewareGroups = [
'api' => ['throttle:10,3']
];

但是,我想在我的一个控制器中执行某些操作后重置计数(例如在成功登录后)。

我可以看到此中间件使用RateLimiter并且有一个名为clear的公共方法。

问题是,如何使用这个?因为它取决于key中间件的ThrottleRequests

  1. 获取object的{​​{1}}我需要ThrottleRequests的实例
  2. 要获取RateLimiter的对象,我需要RateLimiter的实例。 。
  3. 总而言之,如何使用它是无止境的.. 有什么想法吗?

    由于

1 个答案:

答案 0 :(得分:1)

由于您的问题是用 Laravel v5.5 标记的,因此这里适用:

对于专门的登录尝试:

您可以use the Illuminate\Foundation\Auth\AuthenticatesUsers trait in your controller,因此您可以访问 clearLoginAttempts 方法,该方法使用正确的密钥调用 clear() 实例上的 RateLimiter 方法,而无需提供密钥。

实际上,如果您查看 Illuminate\Foundation\Auth\ThrottlesLogins::clearLoginAttempts() 的实现方式,您会发现一旦您的控制器使用了 $this->throttleKey($request) 特性,就可以通过 AuthenticatesUsers 检索正确的键。

一般来说:

您始终可以使用 app(\Illuminate\Cache\RateLimiter::class) 获取 Illuminate\Cache\RateLimiter 实例,该实例又将包含所有配置的限制器和缓存。问题在于,从这个角度来看,不可能获得缓存键。因此,您必须首先找出密钥的设置位置和方式,以便您可以使用相同的密钥进行重置。

标准 ThrottleRequests 中间件在 handle() 方法中设置密钥,但实际密钥将取决于您的节流配置的位置和方式(例如:它是命名限制器还是仅使用数字参数,被调用 ->by(...) 以明确设置键等)

如果您只需要找到一个特定限制器的键,可能您可以在 handle() 方法中设置一个断点并检查。

你的情况

在您的特定情况下,由于它不是命名限制器,因此 handle() 方法将调用 resolveRequestSignature 来获取密钥。我认为您无法从控制器轻松访问中间件实例。您可以做的是检查该方法如何生成密钥并基本上复制那段代码以复制相同的密钥,但我不建议这样做,因为它是一个肮脏和脆弱的解决方案。 如果你检查,你会看到密钥可以复制为:

if ($user = $request->user()) {
    $key = sha1($user->getAuthIdentifier());
}
elseif ($route = $request->route()) {
    $key = sha1($route->getDomain().'|'.$request->ip());
}

但是在最近的 Laravel 版本中,您可以明确设置密钥,这是一个更干净和可靠的解决方案:


在 Laravel 8 中

现在这个问题已经很老了,大多数人宁愿使用最新版本的 Laravel(2021/02/12 的 v8),所以对他们来说 documentation includes the way to "segment" the limiters 又名。能够根据请求(或会话数据等)为不同的请求应用单独的限制计数器。实际上,by() 方法实际上设置了限制器的 key。因此,您可以设置一个或多个命名限制器,例如:

RateLimiter::for('my_per_ip_limiter', function (Request $request) {
    return Limit::perMinute(100)->by($request->ip());
});

这意味着名为 my_per_ip_limiter 的限制器将使用 IP 作为密钥,因此您可以随时在控制器中调用:

app(\Illuminate\Cache\RateLimiter::class)->clear($request->ip());

重置特定 IP 的限制器。或者获取到目前为止的尝试次数:

$attempts_so_far = app(\Illuminate\Cache\RateLimiter::class)->attempts($request->ip());

实际上,您可以使用请求的任何变量(或会话或其他)代替 IP。

但是(我认为)没有办法区分命名限制器。因此,如果相同的键也用于另一个限制器,它们的点击次数将一起计算*并一起清除。因此,给限制器起一个像 my_per_ip_limiter 这样的名字是很有用的,这样你就可以通过名称将该限制器分配给特定的路由,例如:

Route::post( 'login', 'Auth\LoginController@login' )
       ->middleware('throttle:my_per_ip_limiter');

但是如果你真的需要单独重置命名限制器,你必须使用一个唯一的键,例如在它前面加上一些前缀,例如:

RateLimiter::for('my_other_ip_limiter', function (Request $request) {
    return Limit::perMinute(100)->by('other_'.$request->ip());
});

这可以独立于另一个清除:

// reset my_other_ip_limiter, but not my_per_ip_limiter :
app(\Illuminate\Cache\RateLimiter::class)->clear('other_'.$request->ip()); 

*:一起计算我的意思是它们会加起来,所以如果你将它们中的两个应用于同一个请求,每个请求都会使计数器增加 2!