You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway按用户类别实现限流?或改用AWS API Gateway?

问题

我正在AWS基础设施中实现限流功能,应用请求目前通过Spring Cloud Gateway(已实现熔断、重试、限流及超时控制)处理。当前实现基于请求中的session cookie ID作为Key进行限流,相关代码如下:

限流配置类

@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限流Bean

/**
 * Redis Rate Limited
 */
@Bean
fun redisRateLimiter(): RedisRateLimiter {
    return RedisRateLimiter(10, 20, 1)
}

KeyResolver实现

/**
 * 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)
        }
    }
}

现在我希望按用户类别(如premium、basic、admin_user、normal_user)设置不同的限流规则,请问如何通过Spring Cloud Gateway实现该需求?我了解到AWS API Gateway的Usage Plans功能可实现限流,但同时使用两个网关并不合理。另外,我的Spring Cloud Gateway已集成Auth0 by Okta授权,会获取access token并存储至Redis,理论上可在SCG中验证token获取用户角色以使用不同限流规则,但有人指出双重验证access token是不良实践。我是否应改用AWS API Gateway?恳请提供解决方案建议。


解决方案建议

1. 在Spring Cloud Gateway内实现多规则限流(无需双重验证token)

既然你的SCG已经完成Auth0授权并将access token存储到Redis,无需重复验证token签名,只需直接获取已解析的用户角色即可:

  • 扩展KeyResolver,加入角色维度
    修改KeyResolver,将用户ID+角色作为限流Key(格式如user123:premium),既保证同一用户不同角色的限流规则区分,也能独立统计每个用户在对应角色下的请求次数:
    @Bean
    fun roleBasedKeyResolver(sessionProperties: SessionProperties, redisTemplate: StringRedisTemplate): KeyResolver {
        return KeyResolver { exchange ->
            val sessionId = exchange.request.cookies[sessionProperties.SESSION_COOKIE_NAME]?.first()?.value
            if (sessionId.isNullOrBlank()) {
                logger.warn("No session ID found in cookie.")
                return@KeyResolver Mono.just("")
            }
            // 从Redis读取已缓存的用户角色(假设缓存key为"auth:session:$sessionId:role")
            redisTemplate.opsForValue().getMono("auth:session:$sessionId:role")
                .defaultIfEmpty("normal_user")
                .map { role -> "$sessionId:$role" }
        }
    }
    
  • 自定义RedisRateLimiter,按角色加载限流规则
    继承默认RedisRateLimiter,重写限流逻辑,根据角色从配置文件加载对应参数:
    class RoleBasedRedisRateLimiter : RedisRateLimiter(0, 0, 0) {
        @Value("\${rate.limits.premium.replenishRate:30}")
        private var premiumReplenishRate: Int = 30
        
        @Value("\${rate.limits.premium.burstCapacity:60}")
        private var premiumBurstCapacity: Int = 60
        
        @Value("\${rate.limits.basic.replenishRate:10}")
        private var basicReplenishRate: Int = 10
        
        @Value("\${rate.limits.basic.burstCapacity:20}")
        private var basicBurstCapacity: Int = 20
    
        override fun isAllowed(id: String, config: Config): Mono<Response> {
            // 从限流Key中拆分出角色
            val role = id.split(":").last()
            val (replenishRate, burstCapacity) = when(role) {
                "premium" -> Pair(premiumReplenishRate, premiumBurstCapacity)
                "basic" -> Pair(basicReplenishRate, basicBurstCapacity)
                "admin_user" -> Pair(100, 200)
                else -> Pair(5, 10) // normal_user默认规则
            }
            // 使用对应角色的参数执行限流判断
            return super.isAllowed(id, Config().apply {
                this.replenishRate = replenishRate
                this.burstCapacity = burstCapacity
            })
        }
    }
    
  • 更新全局Filter配置
    将自定义的RoleBasedRedisRateLimiter和roleBasedKeyResolver注入到RequestRateLimiterConfig,替换原有的默认实现即可。

2. 关于双重验证token的规避方案

“双重验证access token是不良实践”的说法合理,你可以通过以下方式避免重复验证:

  • 在Auth0授权过滤器中,将解析后的用户角色直接存入ServerWebExchange的属性中,后续限流逻辑直接从属性读取,无需访问Redis。
  • 如果已将token信息缓存到Redis,直接读取缓存值即可,无需重新解析token签名。

3. 是否需要改用AWS API Gateway?

不建议同时使用两个网关,原因如下:

  • 增加架构复杂度:双网关会带来额外运维成本、请求延迟和故障点。
  • 功能重叠:SCG已具备限流、熔断等核心能力,完全可以通过扩展实现多角色限流需求。
  • 迁移成本高:你已在SCG集成Auth0授权,迁移到AWS API Gateway需要重新配置授权规则,增加不必要的工作量。

如果后续有AWS生态强依赖需求(如与Lambda、CloudWatch深度集成),可考虑逐步迁移,但当前场景下优先扩展SCG实现更高效。

内容的提问来源于stack exchange,提问作者Sachin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:17:03