分布式微服务--GateWay的断言以及如何自定义一个断言

发布于:2025-08-09 ⋅ 阅读:(11) ⋅ 点赞:(0)

📌 一、什么是 Gateway 的断言(Predicates)?

Predicates(断言) 是 Spring Cloud Gateway 中用于匹配请求的条件。只有请求满足断言条件,路由才会生效,转发到下游服务。


🎯 二、常见内置断言类型

断言类型 示例配置 对应断言工厂类 说明
Path Path=/api/** PathRoutePredicateFactory 匹配请求路径(支持通配符)
Method Method=GET MethodRoutePredicateFactory 匹配请求方法(GET、POST等)
Header Header=Auth, \w+ HeaderRoutePredicateFactory 匹配请求头,支持正则
Query Query=token QueryRoutePredicateFactory 匹配 URL 查询参数(?key=value)
Host Host=**.example.com HostRoutePredicateFactory 匹配请求 Host(域名)
Cookie Cookie=sessionId, \w+ CookieRoutePredicateFactory 匹配请求中的 Cookie
RemoteAddr RemoteAddr=192.168.0.1/24 RemoteAddrRoutePredicateFactory 匹配客户端 IP(支持 CIDR)
After After=2025-08-01T00:00:00+08:00 AfterRoutePredicateFactory 匹配某个时间之后的请求
Before Before=2025-09-01T00:00:00+08:00 BeforeRoutePredicateFactory 匹配某个时间之前的请求
Between Between=2025-08-01T00:00:00+08:00, 2025-08-31T23:59:59+08:00 BetweenRoutePredicateFactory 匹配时间范围内的请求
Weight Weight=group1, 80 WeightRoutePredicateFactory 灰度发布、流量权重控制

📦 三、断言使用方式(YAML 示例)

spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: http://localhost:8081
          predicates:
            - Path=/user/**
            - Method=GET

🧱 四、自定义断言的必要性

当内置断言不能满足个性化业务需求时,例如:

  • 用户权限判断

  • 参数动态校验

  • 黑白名单过滤

  • 特定设备访问限制

就需要创建自定义断言。


🔧 五、自定义断言步骤(按请求参数控制)

✅ 目标:

只有当请求参数中 allow=true 时才允许路由。


📄 第一步:创建断言类

注意对于

//是1  return new Predicate<ServerWebExchange>() {
//还是2 return new GatewayPredicate() {
 return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                String value =  exchange.getRequest().getQueryParams().getFirst(config.getParam());
                return config.getExpectedValue() != null && config.getExpectedValue().equals(value);
        }
  • 2.0.x.RELEASE:✅ 有 Predicate<ServerWebExchange>,但没有 GatewayPredicate

  • 2.1.0.RELEASE 及以后:✅ 增加了 GatewayPredicate 接口,用于扩展断言工厂。

package com.example.gateway.predicates;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.function.Predicate;

@Component
public class AllowParamRoutePredicateFactory
        extends AbstractRoutePredicateFactory<AllowParamRoutePredicateFactory.Config> {

    public AllowParamRoutePredicateFactory() {
        super(Config.class);
    }
    //方式一 Lambda 表达式形式
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String value = exchange.getRequest().getQueryParams().getFirst(config.getParam());
            return config.getExpectedValue() != null && config.getExpectedValue().equals(value);
        };
    }
    //方式二 匿名内部类形式
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                String value = exchange.getRequest().getQueryParams().getFirst(config.getParam());
                return config.getExpectedValue() != null && config.getExpectedValue().equals(value);
        }
    };
}


    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "expectedValue");
    }

    @Data
    @NoArgsConstructor
    public static class Config {
        private String param;
        private String expectedValue;
    }
}

🧾 第二步:配置文件中使用

spring:
  cloud:
    gateway:
      routes:
        - id: allow_param_route
          uri: http://localhost:8081
          predicates:
            #这个名称必须与AllowParamRoutePredicateFactory所匹配否则匹配不上
            #也就是说自定义拦截器叫HhRoutePredicateFactory
            #下面也要写成Hh=name,value 
            - AllowParam=allow, true

🧪 第三步:访问测试

  • http://localhost:9000/test?allow=true → 匹配成功,转发到下游服务

  • http://localhost:9000/test?allow=false → 匹配失败,不转发


🧠 六、重点解释:值是怎么传入 config 的?

配置值注入原理:

  1. Spring Boot 自动读取 AllowParamRoutePredicateFactory

  2. 发现其内部的 Config 类中包含字段:paramexpectedValue

  3. 调用方法:

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "expectedValue");
    }
    
  4. YAML 配置:

AllowParam=allow, true

➡️ 会自动赋值为:

config.param = "allow";
config.expectedValue = "true";

🧠 七、两行核心逻辑详解

String value = exchange.getRequest().getQueryParams().getFirst(config.getParam());
return config.getExpectedValue().equals(value);
  • 第一句:从请求参数中取出 config.param 指定的参数名的值

  • 第二句:判断这个值是否等于配置中 expectedValue


🔒 八、为什么写成断言而不是过滤器?

对比项 断言(Predicate) 过滤器(Filter)
作用时机 匹配路由前 路由匹配之后
是否转发 决定是否进入路由 已经进入路由,处理请求/响应
使用目的 控制路由是否生效(入门条件) 日志、限流、鉴权、响应处理等增强功能
推荐用途 参数控制、角色控制、AB测试等 认证授权、限流、Header 修改、响应包装等

✅ 九、总结

项目 内容
自定义断言基类 AbstractRoutePredicateFactory
配置值映射方式 shortcutFieldOrder() 定义参数顺序
触发时机 请求进入网关、匹配路由之前
使用场景 请求参数判断、设备识别、用户等级判断等
与 Filter 区别 Predicate 决定“要不要路由”,Filter 是增强
核心逻辑建议 加入 null 判断,防止 NPE

🧰 十、可扩展场景建议

业务场景 自定义断言建议逻辑
按用户权限分流 从 JWT 中解析权限字段,判断是否匹配
灰度发布 用户 ID 做 hash 取模,实现 10% 的灰度流量
手机访问拦截 通过 User-Agent 判断是否来自移动端
限制访问时间 判断当前时间是否处于营业时间

网站公告

今日签到

点亮在社区的每一天
去签到