Open tsvetiligo opened 4 years ago
There are a couple of ways you might go about this. If some of your actions are authenticated and some are not, you could establish two separate rate limiter behaviors and use only
and except
arrays to specify which actions each should apply two.
The rate limiter behavior applying to authenticated actions on which the user identity is established could reference Yii::$app->user->getIdentity()->id
as the identifier. The rate limiter behavior applying to unauthenticated actions on which the user identity is not established could reference an inline method that returns the IP address, like function($context, $rateLimitId) {return $context->request->getUserIP();}
.
Alternatively, you may prefer to just use your user identity's ID when available and fallback to IP address regardless of the action being called. In this case, you could combine this logic into your own identifier function like:
'identifier' => function($context, $rateLimitId) {
if (!empty(Yii::$app->user->getIdentity()->id)) {
return Yii::$app->user->getIdentity()->id;
}
return $context->request->getUserIP();
}
I apply rate limiter behavior to authenticated actions and get "Trying to get property 'id' of non-object" error. My behaviors() method in the code above. Standard Yii2 rate limiter works fine in this behaviors().
There are a couple of problems with your approach on the User model implementing RateLimitInterface
. One might be issue #3 (if so, see the workaround in that issue).
The other is that your User model probably doesn't have an ID yet, so the $this->getId()
call is probably returning an empty/null value. It gets instantiated like this:
$rateLimit = Yii::createObject([
'class' => 'app\models\User',
]);
You wouldn't expect to be able to get an ID from $rateLimit->getId()
in this case unless your User model does some initialization to a default user record. A more typical way of accessing a User model would involve retrieving a record from the database first, like:
$id = 123;
$user = User::findOne($id);
Then you would expect 123
to be returned by $user->getId()
.
In any case, unless you are wanting to vary the limit
and window
values depending on the request context, you shouldn't need to implement your own instance of RateLimitInterface
. When just the identifier is varying, you should have all the flexibility you need to set the identifier
property of the default rate limit definition:
// logged-in identity's ID
'identifier' => Yii::$app->user->getIdentity()->id,
// IP address
'identifier' => function($context, $rateLimitId) {
return $context->request->getUserIP();
},
// Any other value you want to come up with
'identifier' => function($context, $rateLimitId) {
$identifier = ...; // any other static, dynamic value or algorithm you need
return $identifier;
},
Does the "Trying to get property 'id' of non-object" message refer to the Yii::$app->user->getIdentity()->id
line?
Try wrapping the identity
definition in a function:
'identifier' => function($context, $rateLimitId) {
return Yii::$app->user->getIdentity()->id;
},
That would defer referencing the ID until the rate limit is actually being checked as opposed to when the behavior is being defined.
"Basic Limit Per User ID" from Recipes still don't work if I add wrapping (no error, no rate limit), but if I get example from README.md and add wrapping it works great! Thanx! Please update docs.
I'm glad to hear you got it working. Feel free to submit a pull request on the documentation updates.
I try to use "Basic Limit Per User ID" code from recipes and get "Trying to get property 'id' of non-object" error. I use HttpBearerAuth and user don't login yet then this code runs.
I want to use limit per user if user is logged in, and if not - limit per IP. How can I achieve this behavior?
behaviors() code in my controller:
Also I try to implement RateLimitInterface in my User model, but it don't work at all.
in User model:
in controller: