实战二:网关统一鉴权 + 限流落地

网关是流量总闸,最适合做两件横切的事:统一鉴权(所有请求先验身份)与集中限流(按用户/IP 控配额)。本章在 Spring Cloud Gateway 上落地最小可运行方案:GlobalFilter 校验令牌,Redis 限流器按用户限流。

依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- RequestRateLimiter 依赖响应式 Redis 客户端 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

统一鉴权:GlobalFilter

GlobalFilter 对每个请求生效,先于转发执行。示例校验 Authorization 头(生产通常验 JWT 签名或调认证服务,本例封装在 AuthService,密钥放配置中心):

@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    private final AuthService authService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (!authService.valid(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().writeWith(Mono.just(
                exchange.getResponse().bufferFactory()
                    .wrap("{\"code\":401,\"msg\":\"未登录或令牌失效\"}".getBytes())));
        }
        return chain.filter(exchange); // 白名单路径(登录/健康检查)在 authService 放行
    }

    @Override
    public int getOrder() { return -100; } // 高优先级,先于路由
}

要点:下游默认信任网关转发的身份头,需内网隔离防伪造;过滤器里不要做远程查库等重活,否则它自己成为瓶颈。

集中限流:RequestRateLimiter

多实例共享配额必须用 Redis 令牌桶,三要素:过滤器参数、KeyResolver(限流维度)、Redis。

@Configuration
public class RateLimitConfig {
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> { // 按用户 ID 限流,取不到退回 IP
            String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
            return Mono.just(userId != null ? userId
                : exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
        };
    }
}
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                key-resolver: "#{@userKeyResolver}"
                redis-rate-limiter.replenish-rate: 10   # 每秒补 10 个令牌
                redis-rate-limiter.burst-capacity: 20   # 突发容量 20
                redis-rate-limiter.requested-tokens: 1

超配额默认返回 429。replenish-rate 定稳定速率,burst-capacity 允许短时突发(两者相等即退化为匀速);Redis 不可用时请求会被拒绝,需保证 Redis 高可用或配置降级。

运行与验证

  1. 启动 Redis、Nacos、order-service 与网关。
  2. 不带令牌请求 /api/order/** → 401。
  3. 带合法令牌请求 → 正常转发到 order-service。
  4. 快速连续请求超过 burst-capacity → 429;观察 Redis 中按用户生成的限流 key。
  5. 启动第二实例,验证配额集群共享(同一用户两边合计受限)。

常见问题

  • 过滤器顺序:鉴权过滤器 getOrder 必须小于其他过滤器,否则请求先被转发。
  • KeyResolver 空 Key:全部请求汇聚到一个桶互相挤占,务必有兜底维度。
  • 阈值设定:replenish-rate 依据压测数据(见压测章节),配太小会误杀正常流量。
  • 无状态:鉴权不落本地会话、限流状态在 Redis,多实例才能随意扩。

小结

网关统一鉴权解决“每个服务重复验身份”,集中限流解决“单实例限流管不住总量”。本例骨架(GlobalFilter + Redis 令牌桶 + KeyResolver)可直接扩展:令牌换 JWT 验签、维度换接口+用户组合、叠加灰度头路由,即构成生产网关的核心能力集。

笔记加载中…