Spring Cloud Gateway 过滤器从入门到精通:配置、执行顺序与实战

文章目录

一篇讲透 Gateway 过滤器的文章,涵盖局部过滤器、全局过滤器、配置写法、执行顺序、shortcutFieldOrderStripPrefixWhiteList 实战等核心知识点。


一、前言

Spring Cloud Gateway 是 Spring Cloud 生态中的第二代网关,基于 WebFlux 响应式编程模型。它的核心能力之一就是过滤器(Filter),用于在请求转发前后做各种处理:鉴权、限流、日志、路径重写、白名单校验等。

但很多人在使用时会遇到几个困惑:

  • 局部过滤器和全局过滤器到底有什么区别?
  • YAML 里 WhiteList=admin,test 这种简写是怎么解析的?
  • shortcutFieldOrder() 是干什么的?
  • 多个过滤器一起用时,执行顺序到底怎么排?
  • getOrder() 和配置顺序,谁说了算?

这篇文章一次讲清楚。


二、过滤器的两种类型

对比项 全局过滤器 局部过滤器
实现方式 实现 GlobalFilter(通常再实现 Ordered 继承 AbstractGatewayFilterFactory
生效范围 所有路由,请求必走 只对挂载了它的路由生效
配置位置 无,@Component 自动生效 routes[].filters
能否按路由选择 不能(除非内部自己判断) 能,配在哪个路由就只对哪个路由生效
典型用途 鉴权、全局日志、限流 路径重写、白名单校验、加特定请求头
顺序控制 getOrder() 默认按 YAML 声明顺序;实现 Ordered 则以 getOrder() 为准

一句话总结:

全局过滤器是代码级、对所有路由生效、不能按路由排除;局部过滤器是配置级、按路由挂载、可自由选择给哪些路由用。


三、局部过滤器的标准写法

继承 AbstractGatewayFilterFactory,并定义一个内部 Config 类。

java 复制代码
@Component
public class WhiteListGatewayFilterFactory
        extends AbstractGatewayFilterFactory<WhiteListGatewayFilterFactory.Config> {

    private static final String VALUE = "value";

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

    @Override
    public List shortcutFieldOrder() {
        return Collections.singletonList(VALUE);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String userName = exchange.getRequest().getQueryParams().getFirst("userName");
            if (config.getValue() != null
                    && Arrays.asList(config.getValue().split(",")).contains(userName)) {
                return chain.filter(exchange);
            }
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        };
    }

    public static class Config {
        private String value;
        public String getValue() { return value; }
        public void setValue(String value) { this.value = value; }
    }
}

命名规则(重要)

YAML 里用的名字 = 类名去掉固定后缀 GatewayFilterFactory

类名 YAML 里用的名字
WhiteListGatewayFilterFactory WhiteList
StripPrefixGatewayFilterFactory StripPrefix
CostTimeGatewayFilterFactory CostTime

注意:不是前缀匹配 ,而是去掉固定后缀后做字符串精确匹配 (大小写敏感)。写成 WhiteWhiteListFilterwhiteList 都找不到。

如果用了 @Component("别名") 指定 Bean 名,YAML 里就要用别名:

java 复制代码
@Component("MyWhiteList")
public class WhiteListGatewayFilterFactory extends AbstractGatewayFilterFactory<...> { ... }
yaml 复制代码
filters:
  - MyWhiteList=admin,test

四、YAML 配置的两种写法

WhiteList 为例:

简写形式:

yaml 复制代码
filters:
  - WhiteList=admin,test

完整形式:

yaml 复制代码
filters:
  - name: WhiteList
    args:
      value: admin,test

两者完全等价。

简写能生效的前提是过滤器工厂里实现了 shortcutFieldOrder()


五、shortcutFieldOrder 是什么

中文翻译:简写字段顺序。

含义: 当过滤器在 YAML 里用简写形式(如 WhiteList=admin,test)时,用它来指定这个简写值应该按顺序赋给 Config 类中的哪些字段。

java 复制代码
private static final String VALUE = "value";

@Override
public List shortcutFieldOrder() {
    return Collections.singletonList(VALUE);
}
  • 返回 ["value"] 时,整个 admin,test 都赋给 value 字段。
  • 返回 ["value", "enabled"] 时,WhiteList=admin,true 就会把 admin 赋给 valuetrue 赋给 enabled
  • 返回值里的字符串必须和 Config 字段名完全一致 ,否则注入不进去(字段为 null)。
  • 只有一个字段时,逗号属于值本身,不会被拆分。

args 的含义

args 是传给过滤器工厂 Config 类的参数集合,每个 key 对应 Config 的一个字段,Spring 自动注入。

YAML key Config 字段 setter
value private String value; setValue(...)
parts private int parts; setParts(...)

字段名支持宽松绑定(valueValue 都能绑定)。


六、WhiteList 白名单过滤器实战

配置

yaml 复制代码
filters:
  - WhiteList=admin,test

传递链路

复制代码
WhiteList=admin,test
  → shortcutFieldOrder() 返回 ["value"]
  → Config.setValue("admin,test")
  → apply(config) 里 config.getValue() = "admin,test"
  → split(",") 拆成 ["admin", "test"]
  → contains(userName) 判断

效果

请求 userName 结果
?userName=admin admin 放行
?userName=test test 放行
?userName=guest guest 401
不带参数 null 401

拆分成多项是过滤器运行时 split(",") 做的,不在配置绑定阶段。


七、StripPrefix 路径前缀过滤器

StripPrefix 是 Spring Cloud Gateway 内置的局部过滤器,作用是去掉 URL 路径的前 N 层前缀

源码

java 复制代码
public class StripPrefixGatewayFilterFactory
      extends AbstractGatewayFilterFactory<StripPrefixGatewayFilterFactory.Config> {

   public static final String PARTS_KEY = "parts";

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

   @Override
   public List<String> shortcutFieldOrder() {
      return Arrays.asList(PARTS_KEY);
   }

   @Override
   public GatewayFilter apply(Config config) {
      return (exchange, chain) -> {
         ServerHttpRequest request = exchange.getRequest();
         ServerWebExchangeUtils.addOriginalRequestUrl(exchange, request.getURI());
         String path = request.getURI().getRawPath();
         String[] originalParts = StringUtils.tokenizeToStringArray(path, "/");
         StringBuilder newPath = new StringBuilder("/");
         for (int i = 0; i < originalParts.length; i++) {
            if (i >= config.getParts()) {
               if (newPath.length() > 1) {
                  newPath.append('/');
               }
               newPath.append(originalParts[i]);
            }
         }
         if (newPath.length() > 1 && path.endsWith("/")) {
            newPath.append('/');
         }
         ServerHttpRequest newRequest = request.mutate().path(newPath.toString()).build();
         exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
         return chain.filter(exchange.mutate().request(newRequest).build());
      };
   }

   public static class Config {
      private int parts = 1;

      public int getParts() {
         return parts;
      }

      public void setParts(int parts) {
         this.parts = parts;
      }
   }
}

效果

配置 原始路径 转发路径
StripPrefix=1 /api/order/detail/100 /order/detail/100
StripPrefix=2 /api/order/detail/100 /detail/100
不配置 /api/order/detail/100 /api/order/detail/100

官方示例:请求 /name/blue/red,配置 StripPrefix=2,转发到下游变成 /red

易混点:

  • PrefixPath=/api 是加前缀,方向相反。
  • 层数要数对,去掉层数过多会变成空路径。
  • 如果使用 lb() 负载均衡,需要放在 stripPrefix() 之后,否则生成的 URL 可能不正确。

八、前置过滤器和后置过滤器

这不是两种独立的过滤器类型,而是同一个过滤器内部的两个执行阶段 。在 apply 方法里,chain.filter(exchange) 是分界线:

java 复制代码
@Override
public GatewayFilter apply(Config config) {
    return (exchange, chain) -> {
        // ① 前置逻辑:chain.filter() 之前
        System.out.println("请求进入时执行");

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // ② 后置逻辑:chain.filter() 之后,通过 then 挂载
            System.out.println("响应返回时执行");
        }));
    };
}
阶段 位置 执行时机
前置 chain.filter(exchange) 之前 请求进入、转发给下游之前
后置 chain.filter(exchange).then(...) 下游响应返回之后

执行顺序:多个过滤器的前置按 order 升序执行,后置按 order 降序执行(栈式,像洋葱一样层层包裹)。

复制代码
前置 A → 前置 B → 转发到下游 → 后置 B → 后置 A

常见用途:

  • 前置:鉴权、参数校验、添加请求头、记录开始时间
  • 后置:记录耗时、修改响应头、记录响应状态码

注意 :如果前置逻辑直接 return exchange.getResponse().setComplete()(比如鉴权失败返回 401),请求不会继续往下走,后面的过滤器和后置逻辑都不会执行。


九、执行顺序(重点)

核心规则

所有过滤器(全局 + 局部)进入同一个链,统一排序:

  1. 先按 order 值升序排(值越小越先执行)
  2. order 值相同时,再按声明/加载顺序排

进入按此顺序,返回时逆序(栈式)。

各类过滤器的 order 来源

过滤器 order 来源 默认值
全局过滤器 getOrder() 返回值 必须自己实现,无默认
局部过滤器(实现 Ordered getOrder() 返回值 无默认,由代码决定
局部过滤器(未实现 Ordered 固定 0

局部过滤器没写 order 时

默认 order = 0,同一条路由内多个默认 order 的过滤器,按 YAML 声明顺序执行

yaml 复制代码
filters:
  - StripPrefix=1        # 先执行
  - WhiteList=admin,test # 后执行
  - name: CostTime
    args:
      name: order-service # 最后执行

进入:StripPrefixWhiteListCostTime

返回:CostTimeWhiteListStripPrefix

局部过滤器实现了 Ordered 后

配置仍然决定"哪个路由挂载哪些过滤器",但先后顺序改由 getOrder() 决定,不再看 YAML 声明位置

java 复制代码
@Component
public class WhiteListGatewayFilterFactory
        extends AbstractGatewayFilterFactory<WhiteListGatewayFilterFactory.Config>
        implements Ordered {

    @Override
    public int getOrder() {
        return -1;
    }
}
yaml 复制代码
filters:
  - StripPrefix=1        # order = 0
  - WhiteList=admin,test # order = -1
  - CostTime             # order = 0

实际顺序:

复制代码
WhiteList (-1) → StripPrefix (0) → CostTime (0)

WhiteList 虽写在中间,但 order 最小,最先执行。

全局 + 局部混用

一视同仁,按 order 排队。

示例:

  • 全局 AuthGlobalFilter,order = -1
  • 全局 LogGlobalFilter,order = 100
  • 局部 StripPrefix,order = 0
  • 局部 WhiteList,order = 0

排序结果:

复制代码
AuthGlobalFilter   (-1)
StripPrefix        (0)
WhiteList          (0)   ← 和 StripPrefix 同 order,按声明顺序
LogGlobalFilter    (100)

进入:

复制代码
AuthGlobalFilter → StripPrefix → WhiteList → LogGlobalFilter

返回:

复制代码
LogGlobalFilter → WhiteList → StripPrefix → AuthGlobalFilter

getOrder 常用常量

常量
Ordered.HIGHEST_PRECEDENCE Integer.MIN_VALUE
Ordered.LOWEST_PRECEDENCE Integer.MAX_VALUE

关键结论

  • 全局和局部一视同仁,按 order 排队。
  • order 不同 → 按 order 排,和写在 YAML 哪里无关。
  • order 相同 → 才按声明顺序排。
  • 局部过滤器默认 order = 0,全局过滤器 order 为负则排在局部前面,为正则排在后面。
  • 配置决定"挂不挂载",order 决定"先后顺序",两者是两回事。

十、多个路由只走部分过滤器

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        # 只走 StripPrefix + WhiteList
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1
            - WhiteList=admin,test

        # 走 StripPrefix + WhiteList + CostTime + AddResponseHeader
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1
            - WhiteList=admin,test,guest
            - name: CostTime
              args:
                name: user-service
            - name: MyAddResponseHeader
              args:
                key: X-Gateway-By
                value: gateway

        # 一个都不走
        - id: public-service
          uri: lb://public-service
          predicates:
            - Path=/api/public/**
          filters:
            - StripPrefix=1
请求路径 走的过滤器
/api/order/** StripPrefix → WhiteList
/api/user/** StripPrefix → WhiteList → CostTime → MyAddResponseHeader
/api/public/** StripPrefix

十一、常见内置过滤器速查

过滤器 作用 示例
AddRequestHeader 添加请求头 AddRequestHeader=X-Request-Id, 12345
AddRequestParameter 添加查询参数 AddRequestParameter=userName, John
RemoveRequestHeader 移除请求头 RemoveRequestHeader=X-Debug
StripPrefix 去路径前缀 StripPrefix=1
PrefixPath 加路径前缀 PrefixPath=/api
Retry 请求重试 name: Retry, args: retries: 3
AddResponseHeader 添加响应头 name: AddResponseHeader, args: key/value

十二、易错点清单

  1. shortcutFieldOrder() 的字符串必须和 Config 字段名一致,否则字段为 null
  2. 字段名和 YAML key 必须对得上(支持宽松绑定)。
  3. StripPrefix 层数要数对,别和 PrefixPath 搞混。
  4. StripPrefix 使用 lb() 时要放在负载均衡之后,否则 URL 可能不对。
  5. getOrder() 只影响顺序,不影响是否执行;返回时是逆序。
  6. 局部过滤器默认 order = 0;实现了 Ordered 就以 getOrder() 为准,声明位置不再决定顺序。
  7. 配置决定"挂不挂载",order 决定"先后顺序",两者是两回事。
  8. 自定义过滤器类名去掉 GatewayFilterFactory 才是 YAML 里用的名字。
  9. 简写形式 Filter=value 依赖 shortcutFieldOrder(),不写就只能用完整形式。
  10. 全局和局部混用时统一按 order 排序,order 相同才看声明顺序。

十三、总结

本文围绕 Spring Cloud Gateway 的过滤器,系统梳理了以下内容:

  1. 过滤器分类:全局过滤器 vs 局部过滤器的区别。
  2. 局部过滤器写法 :继承 AbstractGatewayFilterFactory,定义 Config 内部类。
  3. 命名规则 :类名去掉 GatewayFilterFactory 后缀,字符串精确匹配。
  4. 配置写法 :简写形式与完整形式等价,简写依赖 shortcutFieldOrder()
  5. shortcutFieldOrder:指定简写值按顺序赋给哪些字段。
  6. args 含义 :传给 Config 的参数集合。
  7. 实战案例WhiteList 白名单校验、StripPrefix 路径重写。
  8. 执行顺序:先按 order 升序,order 相同再按声明顺序;进入正序,返回逆序。
  9. 前置/后置逻辑 :同一个过滤器内 chain.filter() 前后两个阶段。

掌握这些,基本可以应对 Gateway 过滤器的绝大多数使用场景。

相关推荐
NGINX开源社区7 小时前
NGINX Gateway Fabric 2.6:F5 WAF for NGINX 集成 Gateway API
nginx·gateway·fabric
Raas1008 小时前
AI网关有哪些功能?MAI Gateway(魔芋企业级AI网关)实战能力深度解读
大数据·人工智能·网关·gateway·ai网关·mai gateway·企业级产品
Raas1002 天前
AI网关和LiteLLM区别在哪?MAI Gateway(魔芋企业级AI网关)统一治理方案深度解析
大数据·人工智能·gateway·mai gateway·企业级产品
Raas1002 天前
MAI Gateway(魔芋企业级AI网关)对比分析:AI网关和OpenRouter区别?企业级能力差距一览
java·服务器·网络·人工智能·gateway·ai网关·mai gateway
Raas1002 天前
AI网关和OpenRouter区别在哪?MAI Gateway(魔芋企业级AI网关)统一治理方案深度解析
大数据·人工智能·gateway·ai网关·mai gateway
Forerror20263 天前
深入理解大模型网关是什么:MAI Gateway架构与核心价值解读
架构·gateway
会周易的程序员3 天前
告别 matiec 与 Docker:aiDgePLC Editor 全面拥抱 STVM 字节码编译
物联网·网关·electron·软plc·iec61131·stvm·open plc
会周易的程序员3 天前
aiDgeController软PLC控制通讯协议文档
c++·物联网·网关·iot·ipc·进程间通讯