spring-cloud / spring-cloud/spring-cloud-gateway
Spring Cloud Gateway - How to implement Rate Limiter not by Key, but by Class of User
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 4.9k
- Forks
- 3.5k
- Avg merge
- 20h 57m
- Merged PRs (30d)
- 8
Description
Hi,
I'm currently trying to implement a Rate Limiter on my AWS infrastructure.
Requests for my app currently go through Spring Cloud Gateway (which does circuit breaking, retries, rate limiting, and a timeout)
My rate limiter code looks like this:
@Configuration
internal class RequestRateLimiterConfig(
private val requestRateLimiterGatewayFilterFactory: RequestRateLimiterGatewayFilterFactory,
private val redisRateLimiter: RedisRateLimiter,
private val defaultKeyResolver: KeyResolver
) {
private val logger = LoggerFactory.getLogger(RequestRateLimiterConfig::class.java)
@Bean
fun requestRateLimiter(): GlobalFilter {
// rate limiter filter
val rateLimiterConfig = RequestRateLimiterGatewayFilterFactory.Config().apply {
rateLimiter = redisRateLimiter
keyResolver = defaultKeyResolver
denyEmptyKey = true
statusCode = HttpStatus.TOO_MANY_REQUESTS
emptyKeyStatus = HttpStatus.BAD_REQUEST.name
}
val rateLimiterFilter = requestRateLimiterGatewayFilterFactory.apply(rateLimiterConfig)
return GlobalFilter { exchange, chain ->
val keyMono = defaultKeyResolver.resolve(exchange)
keyMono
.flatMap { key ->
if (key.isNullOrEmpty()) {
// if key is null or empty, return error
logger.warn("Empty session ID detected. Sending error response.")
return@flatMap LocalExceptionHandlers.missingKey(exchange)
} else {
// if key is present, continue with rate limiting
logger.info("Resolved key: $key")
return@flatMap rateLimiterFilter.filter(exchange, chain)
.onErrorResume { e ->
// Handle rate limiting errors and send error response
val status = exchange.response.statusCode
if (status == HttpStatus.TOO_MANY_REQUESTS) {
return@onErrorResume LocalExceptionHandlers.rateLimitExceeded(exchange)
}
Mono.error(e)
}
}
}.then()
}
}
}
/**
* Redis Rate Limited
*/
@Bean
fun redisRateLimiter(): RedisRateLimiter {
return RedisRateLimiter(10, 20, 1)
}
/**
* Default Key Resolver
*/
@Bean
fun defaultKeyResolver(): KeyResolver {
return KeyResolver { exchange: ServerWebExchange ->
val sessionId = exchange.request.cookies[sessionProperties.SESSION_COOKIE_NAME]?.first()?.value
if (sessionId.isNullOrBlank()) {
logger.warn("No session ID found in cookie.")
Mono.just("")
} else {
logger.info("Resolved session ID for Rate Limiting: $sessionId")
Mono.justOrEmpty(sessionId)
}
}
}
It is fairly simple with rate limiting done by a key present in the request (in my case session cookie id)
Now, what if I wanted to have different rate limits, by "classes of users" e.g. (premium, basic, admin_user, normal_user)
How would I do that using SCG? I tried exploring AWS API Gateway as they have something called Usage Plans for Rate limiting, but it didn't make sense having two Gateways, AWS API Gateway + Spring Cloud Gateway
My SCG does do authorization, so does get an access token from Auth0 by Okta, and store that in redis.
In theory, like API Gateway has a Lambda Authorizer, I could verify the token in the SCG, to get e.g. the ROLE of a user, and based on that use different rate limiters.
But I have been told that verifying an access token in both the SCG, and Spring Rest API behind it, is bad practice. (currently the access token sent by the SCG app is verified by the Spring Rest API only (using Spring Security Resource Server. SCG uses Spring Security Client, to get the access token only from Auth0 by Okta)
Or should I just try to use AWS API Gateway?
Any help to the above would be appreciated
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No repository file or test is identified. Start by reviewing RequestRateLimiterGatewayFilterFactory, RedisRateLimiter, KeyResolver, and the Spring Security configuration, then determine where user roles are available and whether Spring Cloud Gateway or AWS API Gateway should own class-based limits; done requires an agreed design with tests or documented behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, java, kotlin, redis, spring, spring-boot
- Domain
- api, authentication, backend, cloud, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100