SpringCloud Alibaba学习(十一)——GateWay网关

网关的作用

微服务网关可以针对项目,实现日志、限流、登录token校验、权限校验、安全架构等作用。

目前使用网关最多的有Zuul、GataWay等。本篇文章主要说明GateWay的配置和使用。如果想学习Zuul的,可以移步我的另外一篇博客:
SpringCloud深入学习(九)——Zuul网关之路由、过滤器、容错回退的配置和使用

nginx实现路由网关

定义两个服务,分别占用70709090端口。使用nginx的80端口,按照请求url中的标识符,进行两个端口的区别请求,只需要在配置文件中,增加如下配置即可实现:
在这里插入图片描述
当请求使用localhost/api-product/xxxxx则反向代理至localhost:7070/xxxxx中。
当请求使用localhost/api-order/xxxxx则反向代理至localhost:9090/xxxxx中。

为什么优先选用GateWay

1、 Netflix 对Zuul1不再维护更新,同时Zuul2 迟迟未发布。
2、GateWay 具有的优势
Spring Cloud Gateway is built on Spring Boot 2.x, Spring WebFlux, and Project Reactor.

Spring Cloud Gateway基于Spring Boot 2.x,Spring WebFlux和Project Reactor构建。

动态路由
支持 Predicate(断言)和 Filter(过滤器)
集成Hystrix断路器功能
GateWay 基于异步非阻塞模型上进行开发的,性能更加好。

GateWay是什么

Zuul是Netflix公司的网关产品,因为部分原因,导致Zuul不再维护更新。
GateWay是Spring自身研发的一款网关组件。
Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,例如:安全性,监视/指标和弹性。

GaeWay网关官方文档

核心概念

路由(Route):网关的基本构建块。它由ID,目标URI,一组断言和一组过滤器组成。如果断言路由为true,则说明请求的url和配置匹配。

断言(Predicate):java8中的断言函数。输入类型是Spring 5.0框架中的 ServerWebExchange。这使您可以匹配HTTP请求中的所有内容,例如请求头或参数等。

过滤器(Filter):一个标准的Spring Web Filter。Spring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter 和 Global Filter。可以针对请求和响应做处理。

如何工作的

在这里插入图片描述

测试项目搭建

版本

<spring.boot.version>2.2.9.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.4.RELEASE</spring.cloud.alibaba.version>

依赖引入

注意:

不要引入org.springframework.boot框架下的spring-boot-starter-web

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

配置文件编写

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

注册中心中注册情况

在这里插入图片描述

断言规则配置讲解

断言 gateway官方文档

Path 携带路径路由

修改Gateway中的application.yml配置信息,如下所示:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后

重启微服务。

由于在测试之前,就创建了一个nacos-product的子服务工程,其占用端口为9101,请求接口中也只做了个简单的返回操作。

直接请求9101微服务接口测试:

http://localhost:9101/product/getProduct/1
在这里插入图片描述

通过gateway请求转发测试:

http://localhost:10000/product/getProduct/3
在这里插入图片描述

发现:

1、配置文件中指定断言path规则- Path=/product/**
2、http://localhost:10000/product/getProduct/3中,10000端口为gateway的服务端口。
3、识别到请求中携带/product/
4、则将其转发至http://localhost:9101/。即http://localhost:9101/product/getProduct/3

也就是说:
识别到请求中包含/product/,则将其定向至指定的服务上去,并携带其后的信息。
在这里插入图片描述
注意:

这里的/product/**,表示在gateway_uri之后第一个/时进行判断。


Query 参数路由

修改gateway中的配置文件,增加query请求参数。此处案例为必须携带token这个参数才能路由。

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            #- Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            - Query=token # 匹配请求中包含token才进行路由(token值是多少则无所谓)

重启gateway微服务。

不携带token时进行请求:

http://localhost:10000/product/getProduct/4在这里插入图片描述

当追加token时的请求:

http://localhost:10000/product/getProduct/4?token=666666在这里插入图片描述

当然,也能对token指定一些匹配规则。
比如,请求必须携带token,切token必须满足abc.这个匹配规则。(.表示匹配任意字符)
修改配置文件,重启gateway服务:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            #- Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            #- Query=token # 匹配请求中包含token才进行路由(token值是多少则无所谓)
            - Query=token,abc. # 匹配请求中包含token(token值是多少则无所谓),且必须满足 abc. (.表示任意字符),此时才能满足路由

再次测试,当不指明token必须是abc.时:

http://localhost:10000/product/getProduct/4?token=7777
在这里插入图片描述

当满足要求时:

http://localhost:10000/product/getProduct/4?token=abc1
在这里插入图片描述

此处需要注意

abc.中这个.表示任意字符,必须携带,且只能一个!否则请求失败!
注意正则表达式

问题:

此处没有配置- Path 路由,为什么也能匹配成功?

回答:

此处测试,只配置了一个routes,如果多个就会出现问题。

如:新增额外的routes属性:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: test-2 # 新增一个配置
          uri: http://www.baidu.com
          predicates: # 断言
            - Path=/testbd/**

        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            #- Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            #- Query=token # 匹配请求中包含token才进行路由(token值是多少则无所谓)
            - Query=token,abc. # 匹配请求中包含token(token值是多少则无所谓),且必须满足 abc. (.表示任意字符),此时才能满足路由。但只能时adc1占用一位,没有其他字符或者多个其他字符则失败
           

再次请求:

在这里插入图片描述

此时则需要指明- Path=属性。

在这里插入图片描述


Method 请求方式路由

增加 nacos-product服务中请求接口,分别指明对应的请求方式,如GETPOST

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * gateway请求接口设定
 */
@RestController
public class GateWayTestController {
    
    
    @Value("${server.port}")
    private String port;

    /**
     * get 请求
     * @param id
     * @return
     */
    @GetMapping("/product/getTest/{id}")
    public String getTest(@PathVariable("id") String id){
    
    
        return "this is product services,get method,port:"+port+"\t id="+id;
    }

    /**
     * post 请求
     * @param id
     * @return
     */
    @PostMapping("/product/postTest/{id}")
    public String postTest(@PathVariable("id") String id){
    
    
        return "this is product services,post method,port:"+port+"\t id="+id;
    }
}

修改gateway中的配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: test-2
          uri: http://www.baidu.com
          predicates: # 断言
            - Path=/testbd/**

        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            #- Query=token # 匹配请求中包含token才进行路由(token值是多少则无所谓)
            #- Query=token,abc. # 匹配请求中包含token(token值是多少则无所谓),且必须满足 abc. (.表示任意字符),此时才能满足路由。但只能时adc1占用一位,没有其他字符或者多个其他字符则失败
            - Method=GET #匹配任意get请求,如果需要匹配多个,则采取","分割,如:- Method=GET,POST

由于请求地址中分别定义了GETPOST两种方式,则此时只需要分别测试就可以发现是否满足配置要求。

请求Post类型接口,且不走gateway!
在这里插入图片描述
当进行Post请求,且采取 gateway路由转发时:
在这里插入图片描述
当进行Get请求,且采取 gateway路由转发时:
在这里插入图片描述


Datetime 路由

时间类型的配置包含- After=- Before=- Between=
比如:

- After=2017-01-20T17:42:47.789-07:00[America/Denver]
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

本次测试只测试- After=

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: test-2
          uri: http://www.baidu.com
          predicates: # 断言
            - Path=/testbd/**

        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            #- Query=token # 匹配请求中包含token才进行路由(token值是多少则无所谓)
            #- Query=token,abc. # 匹配请求中包含token(token值是多少则无所谓),且必须满足 abc. (.表示任意字符),此时才能满足路由。但只能时adc1占用一位,没有其他字符或者多个其他字符则失败
            #- Method=GET #匹配任意get请求,如果需要匹配多个,则采取","分割,如:- Method=GET,POST
            - After=2222-02-03T21:20:00.000+08:00[Asia/Shanghai] # 匹配中国上海 2222.02.03 21:20:00 之后的请求

由于当前时间为2020-02-03 21:19:00,定义时间为2222-02-03显然未到,此时请求接口测试发现如下所示:
在这里插入图片描述
当修改时间为当前时间段之前,如2020-01-01

- After=2020-01-01T21:20:00.000+08:00[Asia/Shanghai] # 匹配中国上海 2020.01.01 21:20:00 之后的请求

再次请求测试:
在这里插入图片描述
此处补充几点知识:

1、Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类.
ZonedDateTime 支持通过时区来设置时间,中国的时区是:Asia/Shanghai
其他国家时间别名参考java.time.ZonedDateTime中的java.time.ZoneId属性参数。
2、+08:00表示指时间和 UTC 时间相差八个小时,时间地区为Asia/Shanghai。
在这里插入图片描述


RemoteAddr 远程地址路由

配置指定的远程ip地址可访问。
当前测试使用的ip地址为:
在这里插入图片描述
修改gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: test-2
          uri: http://www.baidu.com
          predicates: # 断言
            - Path=/testbd/**

        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            - RemoteAddr=192.168.99.106/0 # 只允许指定ip访问,/0表示子网掩码

重启服务,访问测试连接:
在这里插入图片描述
变更ip地址,再次请求测试:

- RemoteAddr=192.168.10.66/0 # 只允许指定ip访问,/0表示子网掩码

在这里插入图片描述


Header 请求头路由

指定必须包含对应请求头标识,才能路由,否则失败!

修改gateway配置文件。

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: test-2
          uri: http://www.baidu.com
          predicates: # 断言
            - Path=/testbd/**

        - id: path_product # 唯一id,名称可以随意定义,但必须保证唯一
          uri: http://localhost:9101 # 目标URI,路由到微服务的地址
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
            - Header=X-Request-Id, \d+  # 请求必须包含 X-Request-Id 请求头,且其值与\ d +正则表达式匹配(即,其值为一个或多个数字),则此路由匹配。

使用postman测试,当不携带 X-Request-Id时:
在这里插入图片描述
携带 X-Request-Id时:
在这里插入图片描述
注意:

携带请求头的参数以定义的正则表达式为主,否则也会请求失败!

动态路由

之前测试时,配置文件中采取写死地址的方式实现测试。但在实际开发中,多个微服务分别部署在不同的服务器上,开发至上线变更时,需要频繁的去修改对应服务的ip和端口信息。

其次,正常的服务器架构,都是采取的集群操作,相同微服务一般会部署到多台服务器上,写死ip+port的方式并不利于实际项目的运营,增加维护负担。此时则可以采取注册中心来实现动态路由配置。

既然使用注册中心,则可以使用服务别名来配置。

本次注册中心使用nacos

动态获取微服务地址端口

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: test-2
          uri: http://www.baidu.com
          predicates: # 断言
            - Path=/testbd/**
            
        # 注册中心动态获取 微服务信息    
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后

nacos-product服务集群化部署。此时nacos中的注册情况如下所示
在这里插入图片描述
访问请求测试:
在这里插入图片描述
在这里插入图片描述
观察gateway子服务控制台日志:
在这里插入图片描述

初次请求时,会从注册中心拉取对应微服务的注册信息!


服务名称转发

实际项目开发中,一个项目会包含多个微服务,当涉及到的微服务成百上千时,是否也需要一个一个的去配置路由规则?

Gateway其实为开发者提供了一种基于注册中心自动配置路由的策略。

修改gateway中的配置文件。

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      discovery:
        locator: # 是否与服务发现组件结合,通过serviceId转发到具体服务实例
          enabled: true # 是否开启基于服务发现的路由规则
          lower-case-service-id: true # 是否将服务名称小写

分别开启nacos-consumernacos-product等微服务,并实现注册至注册中心中。再次进行请求:
在这里插入图片描述

gatewat请求URL / 服务别名 / 具体接口地址

过滤器 Filters

针对gateway的过滤器,其分为两大类,GatewayFilterGlobalFilter。这两者之间的差别如下所示:

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

网关过滤器 GatewayFilter

GatewayFilter官方参考文档

RewritePathGatewayFilterFactory 路径过滤器

Path 路由过滤器可以实现URL的重写,通过重写URL隐藏实际的服务别名的方式,提高服务的安全性。

测试案例:

将请求地址进行修改,依旧可以达到访问的效果。

修改gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            - Path=/product/**,/xiangjiao/**  #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
          filters:
          	# 由于上面的 /xiangjiao/** 是不存在的,所以直接请求携带 /xiangjiao/** 时,会出错,此处使用过滤器将其转化为 /product/**
            # gatewat_url/xiangjiao/product/getProduct/5 转化为 gatewat_url/product/getProduct/5
            # ss 只是个变量,可以定义为其他的
            - RewritePath=/xiangjiao(?<ss>/?.*),$\{
    
    ss} 

由于此时的/xiangjiao/**在子服务中并未做接口,直接请求肯定会报错。
此时采取filters 网关,将携带/xiangjiao/**的请求转化为/product/**

请求测试:

http://localhost:10000/xiangjiao/product/getProduct/5
在这里插入图片描述


PrefixPathGatewayFilterFactory 指定前缀过滤器

为匹配的URI添加前缀。

修改Gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            - Path=/** # 测试请求路由配置 PrefixPath
          filters:
            # 增加请求前缀
            - PrefixPath=/product

重启Gateway微服务。访问如下请求,结果如下所示:

http://localhost:10000/getProduct/5
在这里插入图片描述

注意:

此时的请求由原来的gateway_url/product/getProduct/5,更改为gateway_url/getProduct/5
gateway网关自动为服务追加了前缀’/product’。

StripPrefixGatewayFilterFactory 请求分割过滤

将请求URI,根据/进行分割,可以设定分割掉多少个。

修改gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            - Path=/** # 测试请求路由配置 PrefixPath
          filters:
            # 将 /api/test/product/getProduct/4 分割指定个数,转化为 /product/getProduct/4
            - StripPrefix=2 # 这里的数字表示分割个数,分割以 / 进行

重启gateway网关服务,请求测试:

http://localhost:10000/api/test/product/getProduct/4
在这里插入图片描述

如果配置了- Path=- StripPrefix,此时也需要能识别,该怎么写请求地址?
修改gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            #- Path=/** # 测试请求路由配置 PrefixPath
            - Path=/product_test/** # 测试请求路由配置 PrefixPath,必须以 /product_test 开头
          filters:
            # - Path=/product_test/** 时,如果请求是 gateway_uri/product_test/api/product/getProduct/5
              # 先匹配 gateway_uri/product_test,当匹配适合后,
              # 将请求修改为  gateway_uri/product/getProduct/5,这里的2表示将 /product_test/api 删除
            - StripPrefix=2 # 这里的数字表示分割个数,分割以 / 进行

请求测试:

http://localhost:10000/product_test/api/product/getProduct/5
在这里插入图片描述

注意:

这里的/product_test/**,表示在gateway_uri之后第一个/时进行判断。

SetPathGatewayFilterFactory 模板化请求

将设置的请求路由,替换更改为指定的路由。

修改Gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            - Path=/api/getProduct/{
    
    xiangjiao} # 适配path路由
          filters:
            # 由于 - Path= 设定的请求地址为 /api/getProduct/{xiangjiao}
            # 当匹配完成后,将 {xiangjiao} 部分参数 map保存
            # 并且将 /api/getProduct 部分,转化为 /product/getProduct
            # 即:gateway_uri/api/getProduct/6 转化为 gateway_uri/product/getProduct/6
            # 注意:{xiangjiao} 只是个别名,其次{xiangjiao}为参数,并不能是 /xxx
            - SetPath=/product/getProduct/{
    
    xiangjiao}

重启gateway网关,请求测试:

http://localhost:10000/api/getProduct/6
在这里插入图片描述

AddRequestParameterGatewayFilterFactory 添加请求参数过滤

在请求中增加参数传递。

nacos-product微服务中新增一个api接口(cloudalibaba-Product-9101)。

/**
     * filters 增加请求参数 测试
     * @param id
     * @return
     */
    @RequestMapping("/product/gateway/addRequestParam/{id}")
    public String addRequestParam(@PathVariable("id") String id,@RequestParam(required = false) String flag){
    
    
        return "this is product services,addRequestParam,port:"+port+"\t id="+id+"\t flag="+flag;
    }

修改Gateway配置文件:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            - Path=/api-gateway/**
          filters:
            # RewritePath 将 /api-gateway 移除 ,    $\ 本来是$,但在yaml中必须使用$\
            - RewritePath=/api-gateway(?<ss>/?.*),$\{
    
    ss}
            # 增加请求参数 key = flag,value = 66666
            - AddRequestParameter=flag, 66666

重启微服务和gateway,访问测试:

http://localhost:10000/api-gateway/product/gateway/addRequestParam/2
在这里插入图片描述

SetStatusGatewayFilterFactory 设置状态过滤器

设置返回的状态码。

在修改前,使用上述案例中,返回的状态码为200
在这里插入图片描述
此时,需要其返回的状态码为777

只需要在上述配置中增加一项配置即可- SetStatus=777。完整gateway配置文件如下所示:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址

    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates:
            - Path=/api-gateway/**
          filters:
            # RewritePath 将 /api-gateway 移除 ,    $\ 本来是$,但在yaml中必须使用$\
            - RewritePath=/api-gateway(?<ss>/?.*),$\{
    
    ss}
            # 增加请求参数 key = flag,value = 66666
            - AddRequestParameter=flag, 66666
            - SetStatus=777

重启gateway网关服务,请求测试:

http://localhost:10000/api-gateway/product/gateway/addRequestParam/2
在这里插入图片描述

注意:

严格来说,必须设定有效的Spring HttpStatus

全局过滤器 GatewayFilter

全局过滤器不需要在配置文件中配置,一经配置,则会作用于所有的路由上。
在这里插入图片描述

自定义网关过滤器

自定义网关过滤器需要实现以下两个接口:GatewayFilterorg.springframework.core.Ordered

创建过滤器

import lombok.extern.log4j.Log4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 自定义网关过滤器
 */

public class MyGatewayFilter implements GatewayFilter, Ordered {
    
    
    private Logger log = LoggerFactory.getLogger(MyGatewayFilter.class);
    /**
     * 过滤器业务逻辑
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.info("自定义网关过滤器执行了!!!!!");
        return chain.filter(exchange); // 继续向下执行
    }

    /**
     * 排序<br/>
     * 数字越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
    
    
        return 0;
    }
}

注册过滤器

当写了过滤器,该项过滤器并未针对某些具体的请求进行过滤操作。且也未增加至容器中,此时则需要将自定义过滤器进行注册操作。

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 GatewayRoutesConfig {
    
    

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

注意:

一定要去除yaml中的配置。

重启gateway网关服务,请求测试:

http://localhost:10000/product/getProduct/4
在这里插入图片描述
在这里插入图片描述

自定义全局过滤器(token校验)

全局过滤器会作用至所有的路由上,自定义全局过滤器需要实现以下两个接口:GlobalFilterorg.springframework.core.Ordered

创建过滤器

package cn.linkpower.filter;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 全局token鉴权
 */
@Component // 注册至spring容器中即可,无需像网关过滤器那样还需要注册
public class MyGloabalFilter implements GlobalFilter, Ordered {
    
    
    private Logger log = LoggerFactory.getLogger(MyGloabalFilter.class);
    /**
     * 业务逻辑
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.info("全局过滤器执行了!!!!!");
        // 获取request、response
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        // 获取请求的uri,判断是否需要被拦截
        String uri = serverHttpRequest.getURI().getPath();

        //如果请求地址是login,则不拦截
        if(uri.indexOf("/login") > 0){
    
    
            // 继续执行
            return chain.filter(exchange);
        }

        // 获取指定的token数据
        String token = serverHttpRequest.getQueryParams().getFirst("token");
        // 判断token是否有效(此处为了简化,只判断是否有token这个数据)
        if(StringUtils.isBlank(token)){
    
    
            serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            // 设置回执格式
            serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap("请先登录".getBytes());
            // 不继续向下执行
            return serverHttpResponse.writeWith(Flux.just(dataBuffer));
        }
        return chain.filter(exchange);
    }

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

请求测试:

http://localhost:10000/product/getProduct/4
在这里插入图片描述

如果携带token:

http://localhost:10000/product/getProduct/4?token=66666
在这里插入图片描述

资料参考

gateway 官方文档

Spring Cloud Gateway 实现Token校验

代码下载

github代码地址

猜你喜欢

转载自blog.csdn.net/qq_38322527/article/details/111911000