在微服务架构中,网关(Gateway)作为系统的统一入口,承担着路由转发、鉴权、限流、日志记录等重要职责。Spring Cloud Gateway 提供了强大的过滤器(Filter)机制来实现这些功能。

本文将结合实际开发经验,深入剖析 Spring Cloud Gateway 中两种核心过滤器:GatewayFilterGlobalFilter 的区别、使用方法及底层原理。

1. 核心概念

Spring Cloud Gateway 的 Filter 生命周期分为 Pre(处理请求前)和 Post(处理请求后)。而从作用范围上来分,主要分为两种:

  1. GatewayFilter(局部过滤器)

    • 应用在单个路由或一组路由上。
    • 需要在配置文件中显式配置,或者通过代码创建 RouteLocator 时指定。
    • Spring Cloud Gateway 内置了许多工厂类(GatewayFilterFactory),如 StripPrefixAddRequestHeader 等。
  2. GlobalFilter(全局过滤器)

    • 应用在所有的路由上。
    • 无需在配置文件中针对每个路由配置。
    • 通常用于实现全局性的逻辑,如统一鉴权、全局日志、监控指标等。

2. GlobalFilter 详解与实战

GlobalFilter 是实现全局业务逻辑的首选。它不需要复杂的配置,只需要实现 GlobalFilter 接口并注册为 Spring Bean 即可。

2.1 接口定义

public interface GlobalFilter {
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

2.2 实战案例:全局鉴权过滤器

以下是一个通用的全局鉴权过滤器示例,演示了如何拦截请求、校验 Token 以及处理放行逻辑。

代码实现
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;

/**
 * 自定义全局鉴权过滤器
 * 实现了 GlobalFilter 和 Ordered 接口
 */
@Component
public class CustomAuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 获取请求路径
        String reqPath = exchange.getRequest().getURI().getPath();

        // 2. 白名单放行逻辑 (如登录接口、公共资源等)
        if (isWhiteList(reqPath)) {
            return chain.filter(exchange);
        }

        // 3. 获取 Token (通常从 Header 或 Query Param 中获取)
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        // 或者从参数获取: String token = exchange.getRequest().getQueryParams().getFirst("token");

        // 4. Token 校验
        if (token == null || token.isEmpty()) {
            // 拒绝访问,返回 401 Unauthorized
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        
        // 5. 模拟 Token 验证 (例如校验 JWT 签名或查询 Redis)
        if (!validateToken(token)) {
             // Token 无效,返回 403 Forbidden
             exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
             return exchange.getResponse().setComplete();
        }
        
        // 6. 校验通过,传递给下一个过滤器
        return chain.filter(exchange);
    }

    /**
     * 过滤器优先级,数字越小越优先
     */
    @Override
    public int getOrder() {
        // 设置较高的优先级,确保鉴权尽早执行
        return -1;
    }
    
    /**
     * 判断路径是否在白名单中
     */
    private boolean isWhiteList(String path) {
        // 示例逻辑:放行登录接口和 Swagger 文档
        return path.startsWith("/auth/login") || path.contains("swagger");
    }

    /**
     * 验证 Token 有效性
     */
    private boolean validateToken(String token) {
        // TODO: 这里接入具体的业务逻辑,如 JWT 解析或 Redis 查询
        return true; 
    }
}
关键点解析
  1. 实现接口:必须实现 GlobalFilter。通常也会实现 Ordered 接口来控制执行顺序。
  2. Order 顺序getOrder() 返回的值越小,优先级越高,越先执行。鉴权通常需要较高的优先级。
  3. 放行逻辑:调用 chain.filter(exchange) 将请求传递给下一个过滤器。
  4. 拦截逻辑:不调用 chain.filter,直接设置 Response 状态码并返回 response.setComplete(),从而阻断请求。

3. GatewayFilter 详解与实战

GatewayFilter 更加灵活,可以针对特定的业务场景只对部分路由生效。

3.1 接口定义

public interface GatewayFilter extends ShortcutConfigurable {
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

3.2 使用方式

方式一:配置文件(使用内置过滤器)

这是最常见的使用方式。例如,在 application.yml 中配置:

spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: lb://user-service
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix=1  # 内置 GatewayFilter,去除路径第一节
            - AddRequestHeader=X-Request-Foo, Bar # 添加请求头
方式二:代码配置(自定义 RouteLocator)

如果需要通过代码动态配置路由,可以使用 RouteLocatorBuilder

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("path_route", r -> r.path("/get")
                    .filters(f -> f.addRequestHeader("Hello", "World"))
                    .uri("http://httpbin.org"))
            .build();
}
方式三:自定义 GatewayFilter 工厂

如果需要自定义一个可以在配置文件中使用的 GatewayFilter,需要实现 GatewayFilterFactory

@Component
public class MyCustomGatewayFilterFactory extends AbstractGatewayFilterFactory<MyCustomGatewayFilterFactory.Config> {

    public MyCustomGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // Pre 逻辑
            System.out.println("Pre: Custom GatewayFilter executed");
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // Post 逻辑
                System.out.println("Post: Custom GatewayFilter executed");
            }));
        };
    }

    public static class Config {
        // 配置属性
    }
}

然后在 YAML 中使用:

filters:
  - name: MyCustom

4. 深度对比与原理分析

4.1 GlobalFilter vs GatewayFilter

特性 GlobalFilter (全局过滤器) GatewayFilter (局部过滤器)
作用范围 所有路由 指定的路由
配置方式 声明为 Bean 自动生效 需在 YAML 或 Java Config 中显式配置
典型场景 统一鉴权、全局监控、全局日志 请求重写、限流、熔断、特定业务逻辑
实现难度 简单 (实现接口 + @Component) 中等 (通常需实现 Factory 以便配置)

4.2 底层原理:统一的 GatewayFilterChain

尽管在使用上分为两种,但在 Spring Cloud Gateway 的底层执行过程中,它们是等价的

GlobalFilter 会被 GatewayFilterAdapter 包装成 GatewayFilterChain 可识别的过滤器。
对一个应用来说,GatewayFilter 和 GlobalFilter 是等价的,order 也会按照顺序进行拦截。

核心流程:

  1. 当请求到达网关时,DispatcherHandler 会匹配到对应的 Route
  2. 网关会将该 Route 配置的 GatewayFilter 列表和所有的 GlobalFilter 列表合并。
  3. GlobalFilter 会被适配器 GatewayFilterAdapter 包装成 GatewayFilter
  4. 合并后的过滤器列表会根据 Ordered 接口的返回值进行排序(数字越小越靠前)。
  5. 排序后的列表构建成一个 GatewayFilterChain 链式执行。

排序规则注意事项:

  • 如果 GlobalFilterGatewayFilterorder 值相同,具体的执行顺序是不确定的(取决于具体的排序算法实现,通常是稳定的,但建议避免相同 Order)。
  • 建议为不同类型的过滤器规划固定的 Order 范围。

5. 总结

  • GlobalFilter 是处理全局通用逻辑的最佳场所,例如实现统一的入口鉴权。
  • GatewayFilter 提供了细粒度的控制能力,适合针对不同微服务定制不同的请求处理逻辑。
  • 理解它们最终都会合并到同一个过滤链中执行,有助于我们更好地设计系统的过滤器体系,避免顺序冲突。

如果这篇文章对你有帮助,请 点赞、收藏、关注 一波!你的支持是我持续输出高质量技术干货的最大动力!!!

Logo

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

更多推荐