Rate limiting
Silhouette can be combined with play-guard to provide rate-limiting based on the needs of your specific application (note: Silhouette does not hold any dependency to play-guard). Common use cases are things such as throttling by IP, as well as throttling a computationally expensive endpoint for an authenticated user not to spam refresh.
The following is an example for building a rate limiting action for a user that is already logged In:
/**
* A Limiter for user logic.
*/
object UserLimiter {
/**
* A Rate limiter Function for.
*
* @param rateLimiter The rate limiter implementation.
* @param reject The function to apply on reject.
* @param requestKeyExtractor The Request Parameter we want to filter from.
* @param actorSystem The implicit Akka Actor system.
* @tparam K the key by which to identify the user.
*/
def apply[T <: Env, R[_] <: SecuredRequest[T, _], K](rateLimiter: RateLimiter)(
reject: R[_] => Result, requestKeyExtractor: R[_] => K
)(
implicit
actorSystem: ActorSystem
): RateLimitActionFilter[R] with ActionFunction[R, R] = {
new RateLimitActionFilter[R](rateLimiter)(reject, requestKeyExtractor) with ActionFunction[R, R]
}
}
We could then define our own implementation of the filter as follows:
type Secured[B] = SecuredRequest[MyEnv, B]
/**
* A default user filter implementation.
*
* @param ac The Akka Actor System implicitly provided.
*/
def defaultUserFilter(implicit ac: ActorSystem): RateLimitActionFilter[Secured] with ActionFunction[Secured, Secured] = {
(UserLimiter.apply[MyEnv, Secured, UUID](new RateLimiter(10, 1f / 10, "Default User Limiter"))
(_ => Results.TooManyRequests("You've been refreshing too much. Please try again in 10 seconds"), r => r.identity.userID))
}
We can then compose actions for some arbitrary, dependency injected controller as such:
class MyController @Inject()(silhouette: Silhouette[MyEnv])(implicit ac: ActorSystem) extends Controller {
private val defaultUserFilter = UserLimiter.defaultUserFilter
def myAction: Action[AnyContent] = (silhouette.SecuredAction andThen defaultUserFilter).async {
implicit request =>
//..Your custom logic here
}
}
We can also leverage some of the built-in limiters within play-guard. In the following example, we will use the built-in HttpErrorLimitAction
to throttle the number of logins a user from a particular IP can make before they are throttled from logging in entirely within some controller:
/**
* A rate limiter based on failed requests.
*/
private val httpErrorRateLimited: ActionBuilder[Request] =
HttpErrorRateLimitAction(new RateLimiter(2, 1f / 10, "test failure rate limit")) { _ => Results.TooManyRequests("failure rate exceeded") }
def submit = (httpErrorRateLimited andThen silhouette.UnsecuredAction).async { implicit request =>
...
}
Updated less than a minute ago