1. 引言

Spring Cloud Gateway 是 Spring Cloud 生态中基于 Spring WebFlux 和 Project Reactor 构建的 API 网关。它提供了动态路由、限流、熔断、安全认证等核心功能,是现代微服务架构中不可或缺的组件。本文将围绕三个核心主题展开:路由配置基于 Redis 的限流(RedisRateLimiter)以及JWT 认证与授权过滤器的实现,并提供完整的代码示例。

2. 环境与依赖准备

首先,创建一个 Spring Boot 项目,并添加以下核心依赖(以 Maven 为例):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
</code>

确保你的 application.yml 中配置了 Redis 连接信息:

spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0

3. 路由配置

Spring Cloud Gateway 支持两种路由配置方式:基于配置文件的静态路由和基于代码的动态路由。

3.1 静态路由配置(YAML)

application.yml 中定义路由规则:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://USER-SERVICE
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1
        - id: order-service-route
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@userKeyResolver}"

解释:

  • id:路由唯一标识。
  • uri:目标服务地址,lb:// 表示通过服务发现(如 Nacos、Eureka)进行负载均衡。
  • predicates:断言条件,Path 表示路径匹配。
  • filters:网关过滤器链,StripPrefix=1 表示去掉路径前缀的第一段(例如 /api/users/info 转发到用户服务时变为 /info)。

3.2 动态路由配置(Java Config)

通过 @Configuration 类定义路由:

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GatewayRouteConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("product-service", r -> r
                    .path("/api/products/**")
                    .filters(f -> f.stripPrefix(1)
                            .addRequestHeader("X-Gateway-Source", "SpringCloudGateway"))
                    .uri("lb://PRODUCT-SERVICE"))
            .route("auth-service", r -> r
                    .path("/auth/**")
                    .uri("lb://AUTH-SERVICE"))
            .build();
}
}

4. 基于 Redis 的限流(RedisRateLimiter)

Spring Cloud Gateway 内置了 RequestRateLimiter 过滤器,可与 Redis 配合实现分布式限流。

4.1 配置限流过滤器

在路由配置中启用限流,并指定 key 解析器:

filters:
  - name: RequestRateLimiter
    args:
      redis-rate-limiter.replenishRate: 10   # 每秒允许的请求数(令牌桶填充速率)
      redis-rate-limiter.burstCapacity: 20   # 令牌桶容量(瞬时最大并发)
      key-resolver: "#{@userKeyResolver}"    # 限流键解析器 Bean 名称

4.2 实现 KeyResolver

限流键决定了如何区分用户或客户端。常见方式包括按用户 ID、IP 或 API 路径限流。

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class RateLimitConfig {
// 按请求 IP 限流
@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(
            exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
}
// 按用户 ID 限流(需从 JWT 或 Session 中提取)
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
// 假设从请求头中获取用户 ID
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (userId == null || userId.isEmpty()) {
userId = "anonymous";
}
return Mono.just(userId);
};
}
// 按 API 路径限流
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getPath().value()
);
}
}

然后在 application.yml 中引用对应的 Bean,例如 key-resolver: "#{@ipKeyResolver}"

4.3 限流响应自定义

当请求被限流时,默认返回 HTTP 429 Too Many Requests。你可以通过自定义异常处理器来返回更友好的响应:

import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
@Order(-1)
@Configuration
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono&lt;Void&gt; handle(ServerWebExchange exchange, Throwable ex) {
    ServerHttpResponse response = exchange.getResponse();
    if (response.isCommitted()) {
        return Mono.error(ex);
    }
// 处理限流异常
if (ex instanceof ResponseStatusException &amp;amp;&amp;amp; 
    ((ResponseStatusException) ex).getStatus() == HttpStatus.TOO_MANY_REQUESTS) {
    response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
    String body = "{\"code\":429,\"message\":\"请求过于频繁,请稍后再试\"}";
    DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
    return response.writeWith(Mono.just(buffer));
}
// 其他异常处理...
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
return response.setComplete();
}
}

5. JWT 认证与授权过滤器

在微服务网关中,通常由网关统一进行 JWT 令牌的验证和用户信息的提取,然后将用户信息传递给下游服务。

5.1 JWT 工具类

首先创建一个 JWT 工具类,用于生成和解析令牌:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtil {
@Value("${jwt.secret:defaultSecretKey}")
private String secret;
@Value("${jwt.expiration:3600000}")
private Long expiration; // 毫秒
private Key key;
@PostConstruct
public void init() {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
}
// 生成令牌
public String generateToken(String username, Map&lt;String, Object&gt; claims) {
return Jwts.builder()
.setClaims(claims)
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
// 解析令牌
public Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
}
// 验证令牌
public boolean validateToken(String token) {
try {
parseToken(token);
return true;
} catch (Exception e) {
return false;
}
}
}

5.2 认证过滤器(GlobalFilter)

实现一个全局过滤器,对需要认证的路径进行 JWT 校验:

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {
private final JwtUtil jwtUtil;
// 白名单路径(无需认证)
private static final List&lt;String&gt; WHITE_LIST = List.of(
"/auth/login",
"/auth/register",
"/public/**"
);
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
public Mono&lt;Void&gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
// 检查是否为白名单路径
if (WHITE_LIST.stream().anyMatch(path::startsWith)) {
    return chain.filter(exchange);
}
// 从请求头获取令牌
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
token = token.substring(7); // 去掉 "Bearer " 前缀
// 验证令牌
if (!jwtUtil.validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 解析用户信息并传递给下游服务
try {
Claims claims = jwtUtil.parseToken(token);
String username = claims.getSubject();
// 将用户信息添加到请求头中
exchange = exchange.mutate()
.request(builder -&gt; builder.header("X-User-Name", username))
.build();
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; // 高优先级,最先执行
}
}

5.3 授权过滤器(自定义 GatewayFilter)

对于需要特定角色或权限的接口,可以创建自定义的 GatewayFilter:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
@Component
public class RoleAuthorizationGatewayFilterFactory extends AbstractGatewayFilterFactory<RoleAuthorizationGatewayFilterFactory.Config> {
public RoleAuthorizationGatewayFilterFactory() {
    super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String requiredRole = config.getRequiredRole();
// 从请求头获取用户角色(实际应从 JWT 解析)
String userRole = exchange.getRequest().getHeaders().getFirst("X-User-Role");
    if (userRole == null || !userRole.equals(requiredRole)) {
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        return exchange.getResponse().setComplete();
    }
return chain.filter(exchange);
};
}
@Override
public List&lt;String&gt; shortcutFieldOrder() {
return Arrays.asList("requiredRole");
}
public static class Config {
private String requiredRole;
public String getRequiredRole() {
return requiredRole;
}
public void setRequiredRole(String requiredRole) {
this.requiredRole = requiredRole;
}
}
}

在路由配置中使用该过滤器:

spring:
  cloud:
    gateway:
      routes:
        - id: admin-route
          uri: lb://ADMIN-SERVICE
          predicates:
            - Path=/api/admin/**
          filters:
            - StripPrefix=1
            - name: RoleAuthorization
              args:
                requiredRole: ADMIN

6. 完整配置示例与测试

6.1 完整的 application.yml

server:
  port: 8080
spring:
application:
name: api-gateway
redis:
host: localhost
port: 6379
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@ipKeyResolver}"
    - id: auth-service
      uri: lb://AUTH-SERVICE
      predicates:
        - Path=/auth/**
      filters:
        - StripPrefix=0
- id: admin-service
  uri: lb://ADMIN-SERVICE
  predicates:
    - Path=/api/admin/**
  filters:
    - StripPrefix=1
    - name: RoleAuthorization
      args:
        requiredRole: ADMIN
jwt:
secret: mySuperSecretKeyForJwtSigning123!
expiration: 3600000

6.2 测试步骤

  1. 启动 Redis 服务。
  2. 启动网关应用。
  3. 使用 Postman 或 curl 测试:
    • 认证测试:访问 /api/users/1 不带 Token,应返回 401。
    • 限流测试:快速连续请求 /api/users/1(带有效 Token),超过 10 次/秒后应返回 429。
    • 授权测试:访问 /api/admin/dashboard 带普通用户 Token,应返回 403;带 ADMIN 角色 Token 应成功。

7. 总结

本文详细介绍了 Spring Cloud Gateway 的三个核心功能:

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐