# spring cloud gateway 实战技巧

# 使用 nacos 做服务注册

spring cloud 版本:2022.0.2 spring cloud albaba 版本:2022.0.0.0-RC1

    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    // 要使用 lb 功能,就需要该库
    implementation 'org.springframework.cloud:spring-cloud-loadbalancer'
    implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
1
2
3
4

# 网关端

spring:
  application:
    name: alto-gateway
  profiles:
    active: dev
  config:
    import: "optional:nacos:${spring.application.name}-${spring.profiles.active}.yml?refresh=true"
  cloud:
    # Nacos帮助文档: https://nacos.io/zh-cn/docs/v2/concepts.html
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        # 注册到 nacos 的指定 namespace,默认为 public
        namespace: dev
        group: DEFAULT_GROUP
    gateway:
      routes:
        - id: app-demo
          uri: lb://app-demo
          predicates:
            - Path=/app-demo/**
          filters:
            - StripPrefix=1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

WARNING

如果要使用 lb:// 功能需要至少满足两项:

  1. 依赖 spring-cloud-loadbalancer
  2. 开启服务发现 @EnableDiscoveryClient
package cn.mrcode.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class AltoGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(AltoGatewayApplication.class, args);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 服务端需要注意的

只需要注册到了服务中心,就可以被访问了

spring:
  application:
    name: app-demo
  profiles:
    active: dev
  config:
    import: "optional:nacos:${spring.application.name}-${spring.profiles.active}.yml?refresh=true"
  cloud:
    # Nacos帮助文档: https://nacos.io/zh-cn/docs/v2/concepts.html
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        # 注册到 nacos 的指定 namespace,默认为 public
        namespace: dev
        group: DEFAULT_GROUP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

WARNING

@EnableDiscoveryClient 是用来从注册中心获取服务列表的

# 一些技巧

# 全局过滤器生效问题

如果你实现了一个 GlobalFilter,并且交给了 IOC 容器,仅仅这样是不生效的 你访问的地址,匹配匹配一个路由规则,全局过滤器才会生效

    @Bean
    @Order
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder
                .routes()
                .route("auth", r -> r.path("/**").uri("http://127.0.0.1:11140"))
                .build();
    }
1
2
3
4
5
6
7
8

原因是它的工作流程如下图所示: image.png

  1. 客户端访问一个地址
  2. 需要找到该地址对应的处理器映射,然后分发给处理器
  3. 再交给过滤器进行处理

# 自定义过滤器如何实现在配置文件中写过滤器器名

下面使用一个白名单功能的过滤器来说明 在 Spring Cloud Gateway 中,您可以使用 org.springframework.util.AntPathMatcher 工具类来匹配路径模式,用于自定义白名单路径。 下面是一个示例 过滤器工厂方法,为您提供了如何使用 AntPathMatcher 的示例:

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 org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;

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

    private AntPathMatcher pathMatcher = new AntPathMatcher();

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            ServerWebExchange mutableExchange = exchange.mutate();
            String requestPath = exchange.getRequest().getPath().toString();
            for (String whitePath : config.getWhiteList()) {
                if (pathMatcher.match(whitePath, requestPath)) {
                    return chain.filter(mutableExchange).then();
                }
            }
            mutableExchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            return mutableExchange.getResponse().setComplete();
        };
    }

    public static class Config {
        private List<String> whiteList;

        public Config() {
        }

        public List<String> getWhiteList() {
            return whiteList;
        }

        public void setWhiteList(List<String> whiteList) {
            this.whiteList = whiteList;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

在上面的示例中,过滤器检查传入请求的路径是否匹配白名单列表中的任何路径。如果匹配,则放行请求,否则返回 HTTP 403 Forbidden 状态码。在 Config 类中,您可以将白名单路径列表作为配置属性进行指定。 您还需要在应用程序配置中声明该工厂类:

spring:
  cloud:
    gateway:
      routes:
        - id: my-route
          uri: https://example.org
          predicates:
            - Path=/foo/**
          filters:
            - WhiteList=  # 自定义的过滤器工厂类名
              args:
                whiteList: /foo/bar,/foo/baz  # 配置白名单路径,使用逗号分隔
1
2
3
4
5
6
7
8
9
10
11
12

在上面的示例中,我们使用自定义的 WhiteList 过滤器(对应的是过滤器工厂 WhiteListGatewayFilterFactory),并将白名单路径 /foo/bar/foo/baz 配置为 args 属性。自定义的过滤器工厂类名需与声明的名称相同。

# 过滤器中如何获取匹配的路由信息

想要在过滤器中获取到 route 相关信息,比如下面这个配置

spring:
	cloud:
    gateway:
      routes:
        - id: app-demo
          uri: lb://app-demo
          predicates:
            - Path=/app-demo/**
          filters:
            - StripPrefix=1
1
2
3
4
5
6
7
8
9
10

想要找到匹配的 route.id 信息,可以如下做

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String routeId = (String)exchange.getAttribute("org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayPredicateMatchedPathRouteIdAttr");
1
2
3

这个是因为在 exchange 中有一个 attributes 的属性,该属性中有哪些信息如下所示 image.png

可能得使用场景:

  • 自定义全局过滤器中,进行 token 验证,但是某些路径肯定是不需要验证的,没有登录也可以访问,所以就需要做一个白名单的,但是下游有很多服务,为了减少路径匹配次数,白名单按服务分类,在这里拿到对应的路由信息就可以进行放行

# 如何往下游服务传递 header

比如验证成功后,需要将 token 对应的用户 id 等信息传递给下游服务,就可以这样做

ServerHttpRequest newRequest = request.mutate()
        .header("sso-user-id", jwtTokenInfo.getSub())
        .build();
return chain.filter(exchange.mutate()
        .request(newRequest)
        .response(response)
        .build());
1
2
3
4
5
6
7