1. 引入依赖

新建网关模块, 启动类正常就行,不用加东西; 然后再pom.xml引入依赖

<!--        网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- nacos 注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
<!--        负载均衡器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

2.配置路由规则

spring.cloud.gateway.routes下配置路由规则;

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.10.128:8848
    gateway:
      routes:
        - id: item-service
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
          # 仅在这个路径生效
          filters:
            - AddRequestHeader=aaa, ylz
      # 这个配置对全部路径生效
      default-filters:
        - AddRequestHeader=all, hahahhaha
  • id: 唯一id,一般用微服务名
  • uri: 转发的目标地址, lb是负载均衡, 去nacos找服务地址
  • predicates: 路由断言, 满足规则则转发, 里面有多种断言
断言名称 参数 作用 示例配置 说明
After 1 个,datetime(ZonedDateTime 格式) 仅匹配指定时间之后的请求 After=2025-09-03T10:00:00+08:00[Asia/Shanghai] 只有指定时间之后的请求才会被路由
Before 1 个,datetime 仅匹配指定时间之前的请求 Before=2025-09-10T23:59:59+08:00[Asia/Shanghai] 指定时间之前的请求会被路由
Between 2 个,均为datetime 仅匹配两个指定时间区间内的请求 Between=2025-09-03T00:00:00+08:00[Asia/Shanghai],2025-09-05T23:59:59+08:00[Asia/Shanghai] 两个时间之间的请求会被路由
Cookie 2 个,Cookie 名、值正则 请求携带指定 Cookie 且值匹配正则 Cookie=userToken,^[a-zA-Z0-9]{16}$ 必须携带指定 Cookie 且值符合正则规则
Header 2 个,请求头名、值正则 请求包含指定请求头且值匹配正则 Header=Content-Type,application/json 请求头存在且值匹配时才路由
Host 多个,域名(支持通配符) 匹配请求 Host 域名 Host=api.example.com,*.test.com 支持精确域名或通配符子域名匹配
Method 多个,HTTP 请求方式 匹配指定请求方法 Method=POST,PUT 仅匹配 GET/POST/PUT/DELETE 等指定请求方式
Path 路径规则列表、是否匹配末尾斜杠 匹配请求访问路径 Path=/api/product/**,true 支持*单通配、**多通配,可控制末尾斜杠匹配
Query 请求参数名、参数值正则(可选) 校验请求 URL 查询参数 Query=token / Query=age,\d+ 可仅校验参数存在,也可校验参数值格式
RemoteAddr 多个 CIDR 格式 IP / 网段 匹配客户端真实请求 IP RemoteAddr=192.168.1.0/24,10.0.0.5/32 支持单 IP 与整网段 IP 拦截匹配
Weight 分组名、权重数值 同路由分组按权重分流流量 Weight=group1,8 / Weight=group1,2 同路由 ID 多服务配置,按权重比例分发请求
XForwardedRemoteAddr 多个 CIDR 格式 IP / 网段 解析代理转发真实客户端 IP 匹配 XForwardedRemoteAddr=192.168.2.0/24

多级反向代理场景专用 IP 校验

  • filters: 路由过滤器, 请求和响应经过时进行处理, 同样也有好几种参数
过滤器名称 作用 核心参数 示例配置 说明 / 适用场景
AddRequestHeader 为请求添加指定的请求头 name, value AddRequestHeader=X-Request-Id, 12345 身份传递、环境标识;下游服务可读取该请求头
AddResponseHeader 为响应添加指定的响应头 name, value AddResponseHeader=X-Response-From, gateway 安全头、缓存控制;可标识响应经过网关处理
RewritePath 重写请求路径 regexp, replacement RewritePath=/api/(?<segment>.*), /service/${segment} 路径转换、版本控制;如 /api/user/1/service/user/1
PrefixPath 为请求路径添加前缀 prefix PrefixPath=/api/v1 路径前缀追加;如 /hello/api/v1/hello
StripPrefix 移除路径中的指定前缀 parts(整数) StripPrefix=1 API 版本管理;如 StripPrefix=1/v1/user/1/user/1
RedirectTo 重定向请求 status, url 路由跳转、维护页面
SetPath 直接设置请求路径 template 简单固定路径重写定向

如果想要让过滤器处理全部的服务可以用 spring.cloud.gateway.default-filters, 配置项与spring.cloud.gateway.routes.filters一样

3. 自定义过滤器

虽然gateway提供的过滤器很多了, 但还是无法满足业务需求, 比如: 在网关统一校验登录状态; 此时就可以使用自定义过滤器

1.全局过滤器-GlobalFilter

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {

    // 类似netty的流水线, ServerWebExchange 是请求和响应的封装, GatewayFilterChain 是过滤器的链
    // 返回值 Mono<Void> 表示异步处理,返回null表示处理完成
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        将处理好的exchange传给下一个过滤器
        System.out.println("MyGlobalFilter: "+ exchange.getRequest().getPath());
        return chain.filter(exchange);
    }

    // 优先级,数字越小优先级越高(可选)
    @Override
    public int getOrder() {
        return 0;
    }
}
  1. @Component成为bean
  2. 实现GlobalFilter接口, 实现Ordered接口(可选, 非必须)
  3. 实现filter方法, 拿到exchange(封装了请求和响应, 这里也可以说是过滤器的上下文), 可以进行处理, 比如校验token; 处理完后用chain(过滤器链)调用下一个过滤器,将上下文exchange传过去
  4. 如果有实现Ordered接口, 就需要实现其getOrder方法, 返回的值越小优先执行

2. 路由过滤器-GatewayFilter

这种过滤器会读取你的配置来定制过滤器, 因此它得是工厂类

无参配置

spring:
  cloud:
    gateway:
      routes:
        - id: item-service
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
          filters:
            - Aaa
      default-filters:
        - Aaa

这里配置项是Aaa, 在创建过滤器工厂类时的命名也有讲究, 应该是: 

{ 配置项 }GatewayFilterFactory, 然后继承抽象类AbstractGatewayFilterFactory<Object>

@Component
public class AaaGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    
    @Override
    public GatewayFilter apply(Object config) {
        // 这样子没法指定顺序, 匿名内部类不能再实现Ordered接口
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("============路由过滤器: " + exchange.getRequest().getPath());
                return chain.filter(exchange);
            }
        };
    }
}

实现其apply方法返回GatewayFilter的实现类, 一般我们直接用匿名内部类去写就行; 但这样又没办法实现Ordered让过滤器优先执行, 这时可以如下: 

    @Override
    public GatewayFilter apply(Object config){
        // 用一个装饰器对象 OrderedGatewayFilter, 传入一个 GatewayFilter 和一个顺序值
        return new OrderedGatewayFilter(new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("============路由过滤器: " + exchange.getRequest().getPath());
                return chain.filter(exchange);
            }
        }, 1);
    }

用一个装饰器对象OrderedGatewayFilter, 传入GatewayFilter实现类排序值, 就能调整过滤器顺序

有参配置

如果我们的配置需要传参数, 就需要这样做

      default-filters:
        - Bbb=hello,ylz,hahaha
@Component
public class BbbGatewayFilterFactory extends AbstractGatewayFilterFactory<BbbGatewayFilterFactory.Config> {

    @Override
    public GatewayFilter apply(Config config) {
        return new OrderedGatewayFilter(new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("============有参数的路由过滤器: 参数有: " + config.getA() + " " + config.getB() + " " + config.getC());
                return chain.filter(exchange);
            }
        }, 2);
    }

    @Data
    public static class Config {
        private String a;
        private String b;
        private String c;
    }

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

    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("a", "b", "c");
    }
  1. 创建一个内部静态类Config, 会用它接受参数, 有几个参数就有多少成员变量
  2. 创建工厂类的无参构造器, 给父类传入Config类的字节码, 再把AbstractGatewayFilterFactory<Object>的泛型改成那个Config类AbstractGatewayFilterFactory<BbbGatewayFilterFactory.Config>
  3. 还需要实现shortcutFieldOrder方法, 返回列表, 列表中的元素是Config的成员名, 且顺序将会是接受配置的参数顺序(第一个对配置参数的第一个, 第二个对配置参数的第二个)

4. 实战示例: 获取登录用户信息, 转发到下游

@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    private final AuthProperties authProperties;
    private final JwtTool jwtTool;

    private static final String AUTHORIZATION = "authorization";
    // spring mvc的匹配器,可以校验路径
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    // 类似netty的流水线, ServerWebExchange 是请求和响应的封装, GatewayFilterChain 是过滤器的链
    // 返回值 Mono<Void> 表示异步处理,返回null表示处理完成
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (isExculde(request.getPath().value())){
            return chain.filter(exchange);
        }

        String token = null;
        List<String> list = request.getHeaders().get(AUTHORIZATION);
        if (list != null && !list.isEmpty()){
            token = list.getFirst();
        }

        // 验证token
        Long userId = null;
        try {
            userId = jwtTool.parseToken(token);
        } catch (Exception e) {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            // setComplete()  到这里就响应回去
            return response.setComplete();
        }

        String userStr = userId.toString();
//        mutate() 修改上下文, 最终返回新的exchange, 需要把新的王下传
        ServerWebExchange newExchange = exchange.mutate()
                .request(a -> a.header("userId", userStr))
                .build();

        // 将新的exchange传给下一个过滤器
        return chain.filter(newExchange);
    }

    private boolean isExculde(String value) {
        for (String path : authProperties.getExcludePaths()){
            // match(), 校验路径
            if (PATH_MATCHER.match(path, value)){
                return true;
            }
        }
        return false;
    }

    // 优先级,数字越小优先级越高(可选)
    @Override
    public int getOrder() {
        return 0;
    }
}

注意这里使用 exchange.mutate() .request(a -> a.header("userId", userStr)) .build(); 返回的是新的exchange, 要将其传递给下个过滤器

Logo

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

更多推荐