目录

一、概述

二、过滤器

二、网关过滤器 GatewayFilter

1. Path 路径过滤器

2. Parameter 参数过滤器

3. Status 状态过滤器

三、全局过滤器 GlobalFilter

1. 自定义过滤器

2. 自定义网关过滤器

3. 统一鉴权


 

 

如果发现本文有错误的地方,请大家毫不吝啬,多多指教,欢迎大家评论,谢谢!


一、概述

本篇文章为系列文章,未读第 上一集 的同学请猛戳这里:Spring Cloud :整合Gateway 学习 (二),下面是讲解网关过滤器 GatewayFilter

二、过滤器

Spring Cloud Gateway 根据作用范围划分为 GatewayFilterGlobalFilter,二者区别如下:

  • GatewayFilter:网关过滤器,需要通过 spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过 spring.cloud.default-filters 配置在全局,作用在所有路由上。
  • GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过 GatewayFilterAdapter 包装成 GatewayFilterChain 可识别的过滤器,它为请求业务以及路由的 URI 转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。

  

二、网关过滤器 GatewayFilter

       网关过滤器用于拦截并链式处理 Web 请求,可以实现横切与应用无关的需求,比如:安全、访问超时的设置等。修改传入的 HTTP 请求或传出 HTTP 响应。Spring Cloud Gateway 包含许多内置的网关过滤器工厂一共有 22 个,包括头部过滤器、 路径类过滤器、Hystrix 过滤器和重写请求 URL 的过滤器, 还有参数和状态码等其他类型的过滤器。根据过滤器工厂的用途来划分,可以分为以下几种:Header、Parameter、Path、Body、Status、Session、Redirect、Retry、RateLimiter 和 Hystrix。

 

springcloud gateway 获取不到请求头信息 springcloud gateway filter_GlobalFilter

 

接下来我们举例说明其中一部分如何使用,其余等大家工作中需要应用时再查询资料学习或者咨询我也可以。

1. Path 路径过滤器

  

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

RewritePathGatewayFilterFactory

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

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
            # 断言,路径相匹配的进行路由 (谓词)
            - Path=/user/** # 路径匹配
          filters:
            #将 /user/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - RewritePath=/user/(?<segment>.*),/$\{segment}

 

 

 访问:http://127.0.0.1:9000/user/user/getUserInfo?userId=1 结果如下:

springcloud gateway 获取不到请求头信息 springcloud gateway filter_GatewayFilter_02

 

PrefixPathGatewayFilterFactory

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

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
            # 断言,路径相匹配的进行路由 (谓词)
            - Path=/** # 所有路径都匹配
          filters:
            # 将 /user/getUserInfo?userId=1 重写为 /user/user/getUserInfo?userId=1
            - PrefixPath=/user

 

 访问:http://127.0.0.1:9000/user/getUserInfo?userId=1 结果如下:

springcloud gateway 获取不到请求头信息 springcloud gateway filter_GatewayFilter_02

 

StripPrefixGatewayFilterFactory

  

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

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
            # 断言,路径相匹配的进行路由 (谓词)
            - Path=/** # 所有路径都匹配
          filters:
            # 将 /api/user/getUserInfo?userId=1 重写为 user/getUserInfo?userId=1
            - StripPrefix=1

访问:http://127.0.0.1:9000/api/user/getUserInfo?userId=1 结果如下:

 

springcloud gateway 获取不到请求头信息 springcloud gateway filter_GatewayFilter_02

 

SetPathGatewayFilterFactory

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

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
             # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api/user/{segment}
          filters:
            # 将 /api/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - SetPath=/user/{segment}

访问:http://127.0.0.1:9000/api/user/getUserInfo?userId=1 结果如下:

springcloud gateway 获取不到请求头信息 springcloud gateway filter_GatewayFilter_02

 

 

2. Parameter 参数过滤器

AddRequestParameter

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

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
             # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api/user/{segment}
          filters:
            # 将 /user/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - RewritePath=/user/(?<segment>/?.*), $\{segment}
            # 在下游请求中添加 flag=1
            - AddRequestParameter=flag, 1

修改用户服务的控制层代码

添加一个 getUserParam 获取网关请求自定义参数接口

@GetMapping("getUserParam")
    @ApiOperation(value = "获取用户参数信息")
    public UserResp getUserParam(HttpServletRequest request,
                                @RequestParam("userId") @ApiParam(name = "userId",value = "用户ID") Long userId
    ) {
        String foo = request.getParameter("foo");
        log.info("getUserHead.resp foo={}",foo);
        UserResp userResp = UserResp.builder()
                .id(userId)
                .createTime(new Date())
                .nickname("change")
                .username("Zou.LiPing")
                .roleName("超级管理员")
                .build();
        return userResp;
    }

访问:http://127.0.0.1:9000/user/user/getUserParam?userId=1 控制台结果如下:

UserController     : getUserHead.resp foo=bar

3. Status 状态过滤器

  

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

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
             # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api/user/{segment}
          filters:
            # 将 /user/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - RewritePath=/user/(?<segment>/?.*), $\{segment}
           # 任何情况下,响应的 HTTP 状态都将设置为 404
            - SetStatus=404 			 # 404 或者对应的枚举 NOT_FOUND

访问:http://127.0.0.1:9000/user/user/getUserParam?userId=1 控制台结果如下:

springcloud gateway 获取不到请求头信息 springcloud gateway filter_Cloud_06

 

三、全局过滤器 GlobalFilter

  

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

 

springcloud gateway 获取不到请求头信息 springcloud gateway filter_spring_07

1. 自定义过滤器

即使 Spring Cloud Gateway 自带许多实用的 GatewayFilter Factory、Gateway Filter、Global Filter,但是在很多情景下我们仍然希望可以自定义自己的过滤器,实现一些骚操作。

2. 自定义网关过滤器

  自定义网关过滤器需要实现以下两个接口 :GatewayFilterOrdered

创建过滤器

 CustomGatewayFilter.java

@Slf4j
@Component
public class CustomGatewayFilter implements GatewayFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("自定义网关过滤器...");
        return chain.filter(exchange);// 继续向下执行
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

注册过滤器 

package com.example.config;

import com.example.filter.CustomGatewayFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 网关路由配置类
 */
@Configuration
public class GatewayRoutesConfiguration {

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes().route(r -> r
                // 断言(判断条件)
                .path("/product/**")
                // 目标 URI,路由到微服务的地址
                .uri("lb://ai-product")
                // 注册自定义网关过滤器
                .filters(new CustomGatewayFilter())
                // 路由 ID,唯一
                .id("product"))
                .build();
    }

}

访问

注释配置文件中所有网关配置,重启并访问:http://localhost:9000/product/getProduct?productId=1 控制台结果如下:

自定义网关过滤器被执行

3. 统一鉴权

接下来我们在网关过滤器中通过 token 判断用户是否登录,完成一个统一鉴权案例。

AuthorizeFilter

/**
 * 认证过滤器
 * @date: 2021/4/22 19:30
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        // 获取 token 参数
        String token = serverHttpRequest.getHeaders().getFirst("token");

        if (StringUtils.isBlank(token)) {
            serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            return getVoidMono(serverHttpResponse, ResultCode.TOKEN_MISSION);
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {

        return 1;
    }

    /** 
     * 错误信息响应到客户端
     * @param serverHttpResponse Response
     * @param resultCode 错误枚举
     * @date: 2021/4/20 9:13
     * @return: reactor.core.publisher.Mono<java.lang.Void> 
     */
    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, ResultCode resultCode) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(Result.failed(resultCode)).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

}

访问

重启并访问:http://localhost:9000/product/getProduct?productId=1

springcloud gateway 获取不到请求头信息 springcloud gateway filter_Cloud_08

我在头部请求 token 参数

springcloud gateway 获取不到请求头信息 springcloud gateway filter_Cloud_09


源码地址

mall-gateway 这个项目

https://gitee.com/gaibianzlp/zlp-mall-demo.git


 

参考链接

1. Spring Cloud 系列之 Gateway 服务网关