php - Laravel 5: how to reset builtin throttle / ratelimiter?

221

Im using Laravel's builtin throttle like this:

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

However, I would like to reset the count after certain action in one of my controllers (for example after successful login).

I can see this middleware usesRateLimiter and that has a public method calledclear.

The problem is, how to use this? Because it depends upon thekey fromThrottleRequests middleware.

  1. To get theobject ofThrottleRequests I need instance ofRateLimiter
  2. To get the object ofRateLimiter, I need instance ofCache. . .

all in all, there is no end to how to use it.. Any idea?

Thanks

101

Answer

Solution:

As your question is tagged with Laravel v5.5, here's what applies there:

For Login attempts specifically:

You can use the , so you would have access to the method, which calls theclear() method on theRateLimiter instance with the correct key without the need to provide the key.

Actually if you look at howIlluminate\Foundation\Auth\ThrottlesLogins::clearLoginAttempts() is implemented, you can see that the right key can be retrieved by$this->throttleKey($request), once your controller uses theAuthenticatesUsers trait.

In general:

You can always get the instance by usingapp(\Illuminate\Cache\RateLimiter::class), which would in turn contain all the configured limiters and the cache as well. The problem is that it is not possible to get the cache keys from this point of view. So you really have to find out where and how the key was set in the first place, so you can use the same key for the reset.

The standard middleware sets the key in the method, but the actual key would depend on where and how your throttling is configured (e.g.: is it a named limiter or only set using the numeric parameters, was->by(...) called on it to set the key explicitly etc.)

If you only need to find the key for one particular limiter, probably you can set a breakpoint in thehandle() method and just check.

Your case

In your particular case, as it is not a named limiter, thehandle() method will call to get the key. I don't think you can easily access the Middleware instance from a Controller. What you can do is to check how that method generates the key and basically copy that piece of code to replicate the same keys, but I'd not recommend that as it is a dirty and fragile solution. Ifyou check, you'll see the key can be reproduced as something like:

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

But in more recent Laravel versions you can explicitly set the key which is much cleaner and reliable solution:


In Laravel 8

Now as the question is fairly old, most people would rather use the latest version of Laravel (v8 as of 2021/02/12), so for them the documentation includes the way to "segment" the limiters aka. become able to apply separate limit counters for different requests based on the request (or session data, etc.). In fact theby() method actually sets thekey of the limiter. So you can set up one or more named limiters like:

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

This means the limiter namedmy_per_ip_limiter will use the IP as the key, so any time in your controllers you could call:

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

to reset the limiter for a particular IP. Or to get the number of attempts so far:

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

Indeed instead of IP you could use any variable of the request (or session or whatever).

However there is no way (I think) to differentiate between the named limiters. So if the same key is used for another limiter as well, their hits will be counted together* and clear together. So giving a name likemy_per_ip_limiter to the limiter is only useful so you can assign that limiter to particular routes by the name, e.g.:

Route::post( 'login', 'Auth\[email protected]' )
       ->middleware('throttle:my_per_ip_limiter');

But if you really need named limiters to reset individually, you have to use a unique key, for example prefixing it with something, e.g.:

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

This can be cleared independently from the other one:

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

*: By counted together I mean they would add up, so if you apply two of them to the same request, each single request will bump the counter by 2!

People are also looking for solutions to the problem: php - Laravel: Change filename when uploading using store()

Source

Didn't find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Ask a Question

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

Similar questions

Find the answer in similar questions on our website.