Spring-Cloud-Gateway过滤器

6/29/2022 微服务

# Spring Cloud Gateway Filter

前述

此前笔记提到过,Spring Cloud Gateway根据作用范围划分为GatewayFilterGlobalFilter

GatewayFilter:网关过滤器

需要通过spring.cloud.routes.filters配置在具体路由下,只作用在当前路由上或通过`spring.cloud.default-filters``配置在全局,作用在所有路由上。

GlobalFilter :全局过滤器

不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。

gateway9

# Path 路径过滤器

RewritePathGatewayFilterFactory过滤器:可以实现URL重写,通过重写URL可以实现隐藏实际路径提高安全性,易于用户记忆和键入,易于被搜索引擎收录等优点。

RewritePath网关过滤器工厂采用路径正则表达式参数和替换参数,使用Java正则表达式来灵活地重写请求路径。

# 实现方式如下:

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: payment_routh3
          uri: lb://nacos-order-consumer
          predicates:
            - Path=/consumer/**,/api-gateway/**
          filters:
             #将/api-gateway/consumer/1 重写为/consumer/1
            - RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# PrefixPathGatewayFilterFactory过滤器:

PrefixPath网关过滤器工厂为匹配的URI添加指定前缀。

gateway:
  routes:
    - id: payment_routh3
      uri: lb://nacos-order-consumer
      predicates:
    	- Path=/**
      filters:
      	#将 /1 重写为  /consumer/1
    	- PrefixPath=/consumer
1
2
3
4
5
6
7
8
9

# StripPrefixGatewayFilterFactory过滤器:

StripPrefix网关过滤器工厂采用一个参数StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数。

gateway:
      routes:
        - id: payment_routh3
         uri: lb://nacos-order-consumer
         predicates:
           - Path=/**
         filters:
         #将 /api/123/product/1 重写为 /product/1
           - StripPrefix=2   # 分割几个,以斜杆计算
1
2
3
4
5
6
7
8
9

# SetPathGatewayFilterFactory过滤器:

SetPath网关过滤器工厂采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法,使用了SpringFramework 中的uri模板,允许多个匹配段。

gateway:
      routes:
        - id: payment_routh3
         uri: lb://nacos-order-consumer
         predicates:
           - Path=/consumer/**,/api/consumer/{segment}
         filters:
         #将 /api/consumer/1 重写为 /consumer/1
           - SetPath=/consumer/{segment}
1
2
3
4
5
6
7
8
9

# Parameter参数过滤器

AddRequestParameter网关过滤器工厂会将指定参数添加至匹配到的下游请求中。

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: payment_routh3
          uri: lb://nacos-order-consumer
          predicates:
            - Path=/consumer/**,/api-gateway/**
          filters:
             #将/api-gateway/consumer/1 重写为/consumer/1
            - RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
            - AddRequestParameter=flag,1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Status状态过滤器

Setstatus网关过滤器工厂采用单个状态参数,它必须是有效的Spring HttpStatus。它可以是整数404或枚举NOT_FOUND的字符串表示。

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: payment_routh3
          uri: lb://nacos-order-consumer
          predicates:
            - Path=/consumer/**,/api-gateway/**
          filters:
             #将/api-gateway/consumer/1 重写为/consumer/1
            - RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
            # 任何情况下,响应的HTTP 状态都设置为404
            - Setstatus=404   # 404 或者对于的枚举 NOT_FOUND
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 全局过滤器GlobalFilter

/**
 * @ClassName: CustomGlobalFilter
 * @Description: 自定义全局过滤器进行权限验证,验证token是否合法
 * @author: LiJunYi
 */
@Component
@Slf4j
public class AccessGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("####自定义全局过滤器####");
        // 获取请求参数
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        // 业务逻辑处理
        if (null == token)
        {
            log.warn("token is null ...");
            return setUnauthorizedResponse(exchange,"非法用户");
        }
        return chain.filter(exchange);
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg)
    {
        ServerHttpResponse response = exchange.getResponse();
        /*响应类型*/
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        /*响应状态码*/
        response.setStatusCode(HttpStatus.UNAUTHORIZED);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(AjaxResult.error(msg)));
        }));
    }

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     *
     * @return int
     */
    @Override
    public int getOrder() {
        return 1;
    }
}
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
47
48
49

# 自定义全局异常处理

/**
 * 用于网关的全局异常处理
 * @Order(-1):优先级一定要比ResponseStatusExceptionHandler低
 */
@Slf4j
@Order(-1)
@Component
@RequiredArgsConstructor
public class GlobalErrorExceptionHandler implements ErrorWebExceptionHandler {

    private final ObjectMapper objectMapper;

    @SuppressWarnings({"rawtypes", "unchecked", "NullableProblems"})
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        // JOSN格式返回
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        if (ex instanceof ResponseStatusException) {
            response.setStatusCode(((ResponseStatusException) ex).getStatus());
        }

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            try {
                //todo 返回响应结果,根据业务需求,自己定制
                CommonResponse resultMsg = new CommonResponse("500",ex.getMessage(),null);
                return bufferFactory.wrap(objectMapper.writeValueAsBytes(resultMsg));
            }
            catch (JsonProcessingException e) {
                log.error("Error writing response", ex);
                return bufferFactory.wrap(new byte[0]);
            }
        }));
    }
}

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