当前位置: 首页 > news >正文

【Spring Cloud】深入探索统一网关 Gateway 的搭建,断言工厂,过滤器工厂,全局过滤器以及跨域问题

文章目录

  • 前言
    • 为什么需要网关以及网关的作用
    • 网关的技术实现
  • 一、Gateway 网关的搭建
    • 1.1 创建 Gateway 模块
    • 1.2 引入依赖
    • 1.3 配置网关
    • 1.4 验证网关是否搭建成功
    • 1.5 微服务结构分析
  • 二、Gateway 断言工厂
    • 2.1 Spring 提供的断言工厂
    • 2.2 示例:设置断言工厂
  • 三、Gateway 路由过滤器及其工厂
    • 3.1 路由过滤器 GatewayFilter
    • 3.2 路由过滤器工厂 GatewayFilter Factory
    • 3.3 示例:添加过滤器工厂
    • 3.4 默认过滤器
  • 四、Gateway 全局过滤器
    • 4.1 全局过滤器的概念和作用
    • 4.2 GlobalFilter 接口定义
    • 4.3 示例:定义全局过滤器进行用户身份验证
  • 五、过滤器链的执行顺序
    • 5.1 过滤器链执行过程
    • 5.2 过滤器的排序规则
    • 5.3 为什么三种不同的过滤器可以进行排序形成过滤器链
  • 六、跨域问题
    • 6.1 什么是跨越问题
    • 6.2 演示跨越问题
    • 6.3 使用 Gateway 解决跨域问题
    • 6.3 使用 Gateway 解决跨域问题


前言

为什么需要网关以及网关的作用

在微服务架构中,网关是至关重要的组件,具有多重职责,为整个系统提供了一系列关键功能。从下面的微服务结构图中,我们可以明确网关的几项主要作用:

微服务结构图:

  1. 请求过滤与安全:
    用户的所有请求首先经过网关,这使得网关成为系统的第一道防线。通过对传入请求的过滤、验证和安全策略的实施,网关确保只有合法的请求能够访问内部的微服务。

  2. 路由与负载均衡:
    网关具有路由功能,能够将收到的请求正确地分发给相应的微服务。这种路由机制使得系统更加灵活,同时,网关还结合了负载均衡的特性,确保各个微服务能够平衡地处理请求,提升系统的性能和可用性。

  3. 统一的接入点:
    作为系统的入口,网关提供了一个统一的接入点,简化了客户端与微服务之间的通信。客户端只需与网关进行交互,而无需直接与各个微服务打交道,从而降低了系统的复杂性。

  4. 请求转换与聚合:
    在实际应用中,某些信息可能分布在多个微服务中。网关的聚合和转换功能使得它能够从多个微服务中收集数据,然后以符合客户端期望的格式返回。此外,网关还可以处理请求和响应的转换,以适应不同微服务的接口。

  5. 请求限流与熔断:
    网关在系统入口处实施请求限流和熔断机制,以防止不良请求对整个系统造成影响。通过在网关层面进行控制,系统能够有效地抵御过载和故障,提高整体的稳定性。

综合而言,网关在微服务架构中扮演了关键角色,通过提供统一入口、安全性、路由、负载均衡等功能,为整个系统的可维护性、可伸缩性和可用性奠定了基础。

网关的技术实现

在Spring Cloud中,实现网关的两种主要方式是使用ZuulGateway。下面简要介绍它们的特点和区别:

1. Zuul

Zuul是一个基于Servlet的网关实现,它在Spring Cloud中充当了路由和过滤器的角色。主要特点包括:

  • 阻塞式编程: Zuul采用阻塞式的处理方式,即每个请求都会在一个单独的线程中处理,等待响应完成后才能继续处理其他请求。

  • 功能全面: Zuul不仅提供了路由功能,还支持请求的过滤、认证、授权等多种功能。这使得它成为一个功能较为完备的网关方案。

2. Spring Cloud Gateway

Spring Cloud Gateway是基于 Spring 5 中引入的 WebFlux 框架的响应式编程实现。与 Zuul 相比,它具有以下特点:

  • 响应式编程: Gateway 采用响应式编程模型,利用反应式流处理请求。这使得它能够更高效地处理大量并发请求,具备更好的性能。

  • 简化的过滤器链: Gateway 引入了全局过滤器、路由断言和过滤器工厂等概念,使过滤器的配置更为灵活。相较于 Zuul,Gateway 提供了更清晰、简洁的过滤器链定义。

  • 内置断言支持: Gateway 内置了多种路由断言,可以根据请求的各种属性进行路由,提供了更强大的路由功能。

  • 动态路由: Gateway 支持动态路由配置,可以在运行时添加、删除路由规则,灵活适应微服务架构的变化。

总体而言,选择使用Zuul还是 Spring Cloud Gateway 取决于具体需求和项目的性能要求。Zuul 在功能上较为全面,而 Spring Cloud Gateway 则更适用于对性能和响应式编程有较高要求的场景。

一、Gateway 网关的搭建

在理解了为什么需要网关以及网关的作用后,我们可以尝试在 cloud-demo 中搭建一个 Gateway 网关。

1.1 创建 Gateway 模块

首先,在项目中创建一个新的模块 gateway,作为我们的网关服务。

然后,为Gateway 模块创建合适的包,并创建一个GatewayApplication启动类:

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

1.2 引入依赖

pom.xml 文件中引入 Gateway 网关和 Nacos 注册中心的依赖。

<!-- 网关依赖 -->
<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>

在上述 pom.xml 文件中你引入了 Spring Cloud Gateway 和 Nacos 服务发现的依赖。这两个依赖的作用:

  1. spring-cloud-starter-gateway 这是 Spring Cloud Gateway 的启动器依赖,它包含了构建 Gateway 时所需的核心依赖和配置。

  2. spring-cloud-starter-alibaba-nacos-discovery 这是阿里巴巴的 Nacos 服务发现的启动器依赖。它为项目提供了与 Nacos 服务注册和发现相关的功能。

通过这两个依赖,就可以在 Spring Cloud 中轻松搭建起一个网关服务,并使用 Nacos 作为服务注册与发现的工具。在微服务体系结构中,Nacos 的作用是集中管理和发现各个微服务,使它们能够相互协作。

1.3 配置网关

application.yml 配置文件中,编写网关的路由配置和 Nacos 的地址信息。

server:port: 10010 # 网关端口
spring:application:name: gatewaycloud:nacos:server-addr: localhost:8848 # Nacos 地址gateway:routes: # 网关路由配置- id: user-service # 路由 ID,自定义的唯一即可# uri: http://127.0.0.1:8081 # 固定的地址uri: lb://userservice # 路由的目标地址,其中 lb 代表 loadBalance 负载均衡,然后是服务的名称predicates: # 路由断言,也就是判断请求是否符合路由规则的条件- Path=/user/** # 这个是按照路径匹配,只要以"/user/"开头的请求就符合要求,即会路由到 userservice 服务- id: order-serviceuri: lb://orderservicepredicates:- Path=/order/**

在上述 application.yml 中,配置了网关的路由规则和 Nacos 的地址信息。对这个配置文件各个部分的说明:

  • server.port 设置了网关服务的端口为 10010

  • spring.application.name 将网关服务的应用名称设置为 gateway

  • spring.cloud.nacos.server-addr 配置了 Nacos 注册中心的地址为 localhost:8848

  • spring.cloud.gateway.routes 定义了两个网关路由规则。

    • 路由规则1 (user-service):

      • id 路由的唯一标识,这里设置为 user-service
      • uri 设置了路由的目标地址为 lb://userservice,其中 lb:// 表示使用负载均衡,后面跟着服务的名称 userservice
      • predicates 路由断言,指定了只有请求路径以 /user/ 开头才会匹配到这个路由规则。
    • 路由规则2 (order-service):

      • id 路由的唯一标识,这里设置为 order-service
      • uri 设置了路由的目标地址为 lb://orderservice,同样使用了负载均衡,后面是服务的名称 orderservice
      • predicates 路由断言,指定了只有请求路径以 /order/ 开头才会匹配到这个路由规则。

这样配置的结果是,当请求进入网关时,如果路径以 /user/ 开头,它将被路由到 userservice 服务;如果路径以 /order/ 开头,它将被路由到 orderservice 服务。这样通过网关,可以在不暴露内部服务地址的情况下,统一管理和调度服务。

1.4 验证网关是否搭建成功

通过上述网关的搭建工作之后,我们可以启动 gateway 服务,然后在浏览器中访问 10010 端口,查看是否能够成功访问 userserviceorderservice 服务:


此时发现分别访问这两个微服务都成功了,也就证明了我们的网关也搭建成功了。

1.5 微服务结构分析

通过以上步骤,我们创建了一个简单的 Gateway 网关,并配置了一个基本的路由规则,将请求转发到用户服务。整个 cloud-demo 微服务的结构如下图所示:

这个微服务结构图很好地描述了整个系统的架构和流程。以下是对图中各个部分的详细解释:

  1. Gateway 网关:

    • 网关是整个系统的入口点,它接收外部请求并将其路由到不同的微服务。
    • 网关的端口为 10010,并通过路由规则确定请求的目标微服务。
  2. 用户服务 (userservice) 微服务:

    • 用户服务处理与用户相关的请求,例如用户信息的获取和操作。
    • 用户服务通过 Nacos 注册到服务发现中心,以便网关可以发现和路由请求到该服务。
  3. 订单服务 (orderservice) 微服务:

    • 订单服务处理与订单相关的请求,例如订单的创建和查询。
    • 与用户服务类似,订单服务也通过 Nacos 注册到服务发现中心。
  4. Nacos 注册中心:

    • Nacos 提供服务注册和发现功能,允许微服务注册并发现其他服务的实例。
    • 网关和微服务都通过 Nacos 注册中心来获取服务实例的列表,以实现负载均衡和动态路由。
  5. 请求流程:

    • 用户通过浏览器访问网关的路径,例如 http://127.0.0.1:10010/user/1
    • 网关根据路由规则确定将请求路由到 userservice 微服务。
    • 网关通过 Nacos 获取 userservice 的服务实例列表。
    • 网关选择一个服务实例,并使用负载均衡策略将请求转发给该实例。
    • userservice 微服务处理请求并返回结果给网关,然后网关将结果返回给用户。

这个微服务结构图清晰地展示了整个系统的工作流程和各个组件之间的关系,有助于理解微服务架构的运作方式。通过网关和服务注册中心的协作,系统能够实现高可用性、负载均衡和动态扩展,以满足不同场景下的需求。

二、Gateway 断言工厂

在上文搭建 Gateway 网关服务的演示中,我们提到了路由断言(predicates)规则,而这些规则实际上是由断言工厂(Predicate Factory)处理的。断言工厂是 Spring Cloud Gateway 提供的一种机制,它们负责将配置文件中的字符串规则解析为具体的路由条件。

2.1 Spring 提供的断言工厂

Spring 提供了多种基本的断言工厂,每个工厂对应一种路由判断的条件。以下是一些常用的断言工厂及其示例:

名称说明示例
After请求是否在某个时间点之后。After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before请求是否在某个时间点之前。Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between请求是否在某两个时间点之间。Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie请求是否包含指定的 Cookie。Cookie=chocolate, ch.p
Header请求是否包含指定的 Header。Header=X-Request-Id, \d+
Host请求是否访问指定的 Host(域名)。Host=**.somehost.org, **.anotherhost.org
Method请求的方法是否为指定的方式。Method=GET, POST
Path请求的路径是否符合指定规则。Path=/red/{segment}, /blue/**
Query请求参数是否包含指定参数。Query=name, Jack
RemoteAddr请求者的 IP 是否在指定范围内。RemoteAddr=192.168.1.1/24
Weight权重处理,通常在负载均衡场景下使用。Weight=group1, group2

这些断言工厂为网关提供了灵活的路由规则配置,使得我们可以根据不同的条件来定制路由策略。在实际使用中,可以根据需求组合使用这些断言工厂,构建复杂而强大的路由规则。

2.2 示例:设置断言工厂

如果我们想要配置某个断言工厂,可以直接参考 Spring 的官方文档:

例如,我们要想设置 After 断言:


这个配置中,对于 orderservice 使用了两个断言:

  1. Path 断言工厂:

    • Path=/order/** 表示请求的路径必须以 /order/ 开头才会匹配这个路由规则。
  2. After 断言工厂:

    • After=2024-01-01T17:42:47.789+08:00[Asia/Shanghai] 表示请求的时间必须在指定的时间点之后,即在 2024 年 1 月 1 日 17:42:47(上海时区)之后。

通过这两个断言,路由规则要求请求路径必须以 /order/ 开头,并且请求的时间必须在 2024 年 1 月 1 日 17:42:47 之后。只有同时满足这两个条件的请求才会匹配到这个路由规则,然后被路由到相应的微服务。

显然当前时间不符合 After 断言工厂,如果我们此时尝试访问,则会出现 404 错误:

那么如果此时将 After 改成 Before 断言工厂呢:


显然当前的时间符合要求,再次重启并访问,便可以成功访问了:

通过上述设置断言工厂的例子,我们就会发现其实设置某个断言工厂非常简单,并且也不需要我们刻意去记某个断言工厂,只需要在使用的时候查阅官方文档即可。

三、Gateway 路由过滤器及其工厂

3.1 路由过滤器 GatewayFilter

GatewayFilter 是网关中提供的一种强大的过滤器,允许对进入网关的请求和微服务返回的响应进行全面的处理。通过网关过滤器,我们可以在请求和响应的不同阶段执行自定义的逻辑,例如添加请求头、修改请求体、记录日志、验证权限等。

流程图解释:

Gateway Filter

  1. 用户的请求首先经过网关的路由进行转发,然后通过一层一层的过滤器链进行过滤。
  2. 过滤器的作用可以包括查看请求头、修改请求体、记录日志等操作,每个过滤器在过滤链中有特定的执行顺序。
  3. 经过一系列的过滤器处理后,请求最终发送给目标微服务。
  4. 微服务处理完请求后,响应的结果同样会经过网关的过滤器链进行处理,最终通过路由返回给用户。

通过合理配置和组合不同的网关过滤器,我们能够实现灵活、高效的请求处理流程。在实际应用中,可以根据具体的业务需求选择合适的过滤器,并有选择性地添加、移除或自定义过滤器,以实现定制化的网关处理逻辑。

3.2 路由过滤器工厂 GatewayFilter Factory

Spring 提供了三十多种不同的路由过滤器工厂,通过 Spring 官网文档 可以查询这些过滤器工厂具体的使用方法:

这些过滤器工厂包括但不限于:

  • AddRequestHeader GatewayFilterFactory: 为请求添加头信息。

    filters:- AddRequestHeader=HeaderName, HeaderValue
    
  • AddResponseHeader GatewayFilterFactory: 为响应添加头信息。

    filters:- AddResponseHeader=HeaderName, HeaderValue
    
  • RewritePath GatewayFilterFactory: 重写请求路径。

    filters:- RewritePath=/foo/(?<segment>.*), /$\{segment}
    
  • SetPath GatewayFilterFactory: 设置请求路径。

    filters:- SetPath=/foo
    
  • RequestRateLimiter GatewayFilterFactory: 请求速率限制。

    filters:- RequestRateLimiter=RateLimitKey, replenishRate, burstCapacity
    

以上是一些常用的过滤器工厂,通过合理配置这些过滤器工厂,我们可以实现对请求和响应的灵活处理。在实际应用中,可以根据具体的需求选择相应的过滤器工厂,并按需组合使用,以实现定制化的网关处理逻辑。

3.3 示例:添加过滤器工厂

在这个示例中,我们可以给所有进入 userservice 服务的请求添加一个请求头:Hello=Hello GatewayFilterFactory!,注意实际上这是一个键值对。

具体的操作则是在gateway服务中修改application.yml文件,给userservice的路由添加过滤器:

验证是否添加请求头成功的方式则可以在 userservice 的请求用户信息方法中新增一个参数,例如:

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "Hello", required = false) String hello) {System.out.println("Hello: " + hello);return userService.queryById(id);
}

在这个 Controller 方法中新增了一个 hello 参数,其来源是 HTTP 的请求头,使用 @RequestHeader 注解标注,并且使用 required 指定了这个参数不是必传的,然后重启 userservice 并在浏览器中进行访问,在控制台上成功打印了“Hello: Hello GatewayFilterFactory!” 日志:

3.4 默认过滤器

通过上面的示例,我们知道了如何给某个微服务的请求加上过滤器,但是如果要给所有服务的请求都加上过滤器该如何操作呢?

可能我们首先会想到分别给各个微服务都加上过滤器的配置项,但是这样就显得非常冗余了。此外,我们还可以使用默认过滤器(default-filters)的方式来给所有的微服务请求都加上相同的过滤器配置,例如:


此时,我们可以尝试把 userservice 中的过滤器配置给注释掉,然后再次重启服务并访问,看看默认过滤器配置是否生效。


此时便说明我们的默认过滤器配置生效了。

四、Gateway 全局过滤器

在前文中,我们已经了解了请求路由的过滤器工厂以及默认过滤器 default-filters 的作用。现在,让我们深入研究另一个强大的概念——全局过滤器 GlobalFilter

4.1 全局过滤器的概念和作用

1. 概念

全局过滤器是 Spring Cloud Gateway 中的一种过滤器类型,它的作用是处理所有进入网关的请求和从微服务返回的响应。与局部过滤器(GatewayFilter)不同,全局过滤器的逻辑需要通过代码来实现,而不是通过配置来定义。

全局过滤器是在整个请求-响应周期中起作用的过滤器。它可以执行一些全局性的任务,如认证、授权、日志记录、性能监控等。全局过滤器不仅可以处理请求,还可以处理从微服务返回的响应,使其成为一个更为强大和灵活的过滤器类型。

2. 作用

  1. 统一处理全局任务:
    全局过滤器可以用于执行与整个微服务架构相关的任务,而不仅仅是单个路由或微服务的请求处理。这使得它非常适合于执行全局性的操作,例如在每个请求中执行身份验证、记录请求日志等。

  2. 修改请求和响应:
    通过全局过滤器,可以在请求到达微服务之前或响应返回客户端之前修改请求或响应。这种能力对于实施诸如请求重写、响应重写、添加头信息等操作非常有用。

  3. 集成外部服务
    全局过滤器还可以用于集成外部服务,例如在请求中调用身份验证服务或其他微服务,以获取必要的信息或执行某些任务。

4.2 GlobalFilter 接口定义

public interface GlobalFilter {  /*** 处理当前请求,有必要的话通过 {@link GatewayFilterChain} 将请求交给下一个过滤器处理** @param exchange 请求上下文,里面可以获取 Request、Response 等信息    * @param chain 用来把请求委托给下一个过滤器    * @return {@code Mono<Void>} 返回标识当前过滤器业务结束*/Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

上述代码展示了 GlobalFilter 接口的定义。其中关键部分的说明如下:

  • filter: 这是 GlobalFilter 接口的核心方法,负责处理当前请求。参数 ServerWebExchange 提供了请求上下文,其中包含了请求和响应的相关信息。参数 GatewayFilterChain 则用于将请求委托给下一个过滤器处理。

  • 返回类型 Mono<Void> 表示当前过滤器的业务逻辑是否已经处理完毕。如果返回的 Mono 完成了,那么表示当前过滤器的任务已完成,请求将继续传递给下一个过滤器。如果 Mono 未完成,请求将被阻塞。

4.3 示例:定义全局过滤器进行用户身份验证

在微服务架构中,用户身份验证是保护服务安全的重要一环。通过定义全局过滤器,我们可以在请求进入网关时对用户身份进行验证,以确保只有合法用户才能访问服务。本示例将展示如何使用 Spring Cloud Gateway 实现一个简单的用户身份验证全局过滤器。

1. 需求说明

我们的需求是拦截请求,判断请求参数中是否包含 authorization 参数,且其值是否为 “admin”。如果满足条件,请求将被放行;否则,请求将被拦截。

2. 实现过程

首先,我们创建一个全局过滤器 AuthorizeFilter,并实现 GlobalFilter 接口:

import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerHttpRequest;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Mono;@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1. 获取请求参数ServerHttpRequest request = exchange.getRequest();MultiValueMap<String, String> params = request.getQueryParams();// 2. 获取参数中的 authorization 参数String authorization = params.getFirst("authorization");// 3. 判断参数值是否等于 adminif("admin".equals(authorization)){// 4. 等于则放行,相当于调用过滤器链中的下一个过滤器return chain.filter(exchange);}// 5. 不等于则拦截// 5.1 设置状态码exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);// 5.2 拦截请求return exchange.getResponse().setComplete();}@Overridepublic int getOrder() {return -1;}
}

在这个过滤器中,我们获取请求参数中的 authorization 参数,并判断其值是否为 “admin”。如果是,请求将继续传递给过滤器链中的下一个过滤器;否则,设置响应状态码为 UNAUTHORIZED,表示请求未被授权,并终止请求处理。

3. 过滤器执行顺序

过滤器的执行顺序可以通过 @Order 注解的方式实现,另外也可以通过实现 Ordered 接口并重写其中的 getOrder 方法。在上述代码中,通过实现 Ordered 接口,我们可以指定过滤器的执行顺序。在这个例子中,通过 getOrder 方法返回 -1,表示这个过滤器的执行顺序较高,会在一般过滤器之前执行。

4. 验证全局过滤器是否生效

重启 gateway 服务,首先直接访问userservice 不带任何参数:

发现访问失败,并且响应码为 401

加上 authorization 参数再进行访问:


此时便可以成功访问了,当然,如果 authorization 参数的值不为 admin 也会访问失败:

通过定义这个简单的全局过滤器,我们实现了对用户身份的基本验证。在实际应用中,用户身份验证通常会更为复杂,可能涉及调用认证服务、检查 token 签名等操作。这个示例提供了一个基本的框架,可以根据实际需求进行扩展,确保微服务的安全性和可靠性。

五、过滤器链的执行顺序

在 Spring Cloud Gateway 中,过滤器链的执行顺序是确保请求经过一系列过滤器并按照特定的规则执行的关键。一般而言,请求进入网关后会遇到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。

5.1 过滤器链执行过程

请求路由后,当前路由过滤器、DefaultFilter、GlobalFilter 会被合并到一个过滤器链(集合)中,并按照排序规则进行排序,然后按照排序后的顺序依次执行。这个过程如下图所示:

过滤器链执行过程

5.2 过滤器的排序规则

为了确保过滤器按照正确的顺序执行,遵循以下排序规则:

  1. 自定义过滤器: 每个自定义过滤器必须指定一个整数类型的 order 值,数值越小,优先级越高,执行顺序越靠前。

  2. GlobalFilter: 全局过滤器通过实现 Ordered 接口或添加 @Order 注解来指定 order 值,由开发者自行指定。值越小,优先级越高。

  3. 路由过滤器和 DefaultFilter: 路由过滤器和 DefaultFilter 的 order 由 Spring 框架指定,默认按照声明顺序从1递增。

  4. 相同 order 值处理: 当过滤器的 order 值相同时,执行顺序为 DefaultFilter > 路由过滤器 > GlobalFilter。具体实现可以查看一些关键类的源码,如 org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()

过滤器链的执行顺序非常关键,它保证了过滤器能够按照开发者期望的顺序执行。通过合并路由过滤器、DefaultFilter 和 GlobalFilter,并按照指定的执行顺序排序,网关可以对请求进行精细控制,实现各种定制化的业务逻辑。在实际开发中,了解和正确使用过滤器链的执行规则是确保网关正常工作的基础。

5.3 为什么三种不同的过滤器可以进行排序形成过滤器链

首先对于路由过滤器和默认过滤器中的AddRequestHeader过滤器工厂来说,它们的底层都是由AddRequestHeaderGatewayFilterFactory 类实现的。

通过查看 AddRequestHeaderGatewayFilterFactory 类的源码可以知道,它们最后最后转化成一个相同类型的对象,即GatewayFilter

但是对于 GlobalFilterGatewayFilter 这两个接口来说,查看它们的源码并没有发现继承关系:


那么是否就意味着它们之间没有关系了呢?其实不然,在 FilteringWebHandler 这个类中有一个静态内部类GatewayFilterAdapter


可以发现这个类实现了GatewayFilter接口,并且其中有一个属性,其类型是GlobalFilter。可以发现可以类的作用是把GlobalFilter适配成了GatewayFilter

因此,这三种过滤器最终都可以当成GatewayFilter,所有它们之间可以按照Ordered接口、@Order注解、默认等指定的顺序放在同一个过滤器链中。

六、跨域问题

6.1 什么是跨越问题

跨域问题是指在 web 应用程序中,由于安全策略的限制,浏览器禁止在一个源(origin)的网页应用程序中发起对另一个源的 HTTP 请求。这个源包括协议、域名、端口的组合,只有当两个请求的源相同时,浏览器才允许跨域请求。

跨域问题主要由以下情况引起:

  1. 不同域名: 当两个请求的域名不同,即使协议和端口相同,也会被认为跨域。例如,www.example.comapi.example.com

  2. 不同端口: 即使在同一个域名下,如果请求的端口不同,也会被认为跨域。例如,example.com:8080example.com:3000

  3. 不同协议: 当一个请求使用 HTTP 协议,而另一个使用 HTTPS 协议时,也会被视为跨域。

跨域问题的存在是为了增加 web 安全性,防止恶意网站利用用户的浏览器向其他网站发起恶意请求,窃取用户信息等。

解决跨域问题的常见方式之一是使用 CORS(跨域资源共享)策略,它允许服务器在响应中附加一些标头,告诉浏览器哪些域名允许跨域访问资源。当浏览器发起跨域请求时,会先发送一个预检请求(OPTIONS 请求)给服务器,服务器根据预检请求的信息来判断是否允许跨域访问。如果服务器返回合适的响应头,浏览器才会允许跨域请求。这种方式可以有效地解决跨域问题,同时保障了网站的安全性。

6.2 演示跨越问题

为了演示跨域问题,我们首先创建一个简单的 HTML 文件 index.html,其中包含了一个使用 Axios 发起的 AJAX 请求:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body>
<pre>
spring:cloud:gateway:globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:'[/**]':allowedOrigins: # 允许哪些网站的跨域请求- "http://localhost:5500"allowedMethods: # 允许的跨域ajax的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期
</pre>
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>axios.get("http://localhost:10010/user/1?authorization=admin").then(resp => console.log(resp.data)).catch(err => console.log(err))
</script>
</html>

说明:

此时,我们假设 http://localhost:10010/user/1 是一个需要进行跨域访问的服务。然后,使用 VS Code 中的 Live Server 插件启动 index.html,然后通过浏览器的开发者工具查看控制台的输出内容:

发现出现了跨越问题,导致不能正常访问userservice的服务。

6.3 使用 Gateway 解决跨域问题

6.3 使用 Gateway 解决跨域问题

为了解决跨域问题,我们可以在 gateway 服务的 application.yml 中新增如下的配置,使用 Spring Cloud Gateway 的跨域配置:

spring:cloud:gateway:globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:'[/**]':allowedOrigins: # 允许哪些网站的跨域请求- "http://localhost:5500"allowedMethods: # 允许的跨域 ajax 的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带 cookiemaxAge: 360000 # 这次跨域检测的有效期

说明:

  • allowedOrigins:允许跨域请求的源,这里设置为 http://localhost:5500
  • allowedMethods:允许的跨域 AJAX 请求方式,包括 GETPOSTDELETEPUTOPTIONS
  • allowedHeaders:允许在请求中携带的头信息,设置为 "*" 表示允许所有头信息。
  • allowCredentials:是否允许携带 Cookie。
  • maxAge:这次跨域检测的有效期,设置为 360000 毫秒。

通过这样的配置,我们告诉 Spring Cloud Gateway 允许指定的源(http://localhost:5500)发起跨域请求,并指定了其他的一些跨域配置。

然后,重新访问 localhost:5500,发现控制台成功输出了访问userservice 的结果了:

相关文章:

【Spring Cloud】深入探索统一网关 Gateway 的搭建,断言工厂,过滤器工厂,全局过滤器以及跨域问题

文章目录 前言为什么需要网关以及网关的作用网关的技术实现 一、Gateway 网关的搭建1.1 创建 Gateway 模块1.2 引入依赖1.3 配置网关1.4 验证网关是否搭建成功1.5 微服务结构分析 二、Gateway 断言工厂2.1 Spring 提供的断言工厂2.2 示例&#xff1a;设置断言工厂 三、Gateway …...

计算机竞赛 题目:基于卷积神经网络的手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…...

关于flink重新提交任务,重复消费kafka的坑

异常现象1 按照以下方式设置backend目录和checkpoint目录&#xff0c;fsbackend目录有数据&#xff0c;checkpoint目录没数据 env.getCheckpointConfig().setCheckpointStorage(PropUtils.getValueStr(Constant.ENV_FLINK_CHECKPOINT_PATH)); env.setStateBackend(new FsStat…...

Win11右键恢复Win10老版本

Win11右键恢复Win10老版本 最近自己更新了windows11的OS,整体感觉都是不错的,但是就是每次右键菜单我都要再次点击下展开更多选项,这对追求极简主义的我,就是不爽, 手动恢复win10操作吧! 第一种:创建文件(简单快速) 1.新建一个resoreRightKey.reg文件,并在里面填入如下代码 W…...

ur机械臂30003端口socket通信踩坑(double类型数据怎么解析)

坑的由来 都知道在网络通信时要把网络字节序转化为主机字节序才行&#xff0c;但是c里的标准库函数ntohl默认是转换32位字节序的数据&#xff0c;也就是说默认是转换float类型的数据&#xff1b;而ur机械臂30003端口发送的是double类型的数据&#xff0c;没法直接用ntohl进行转…...

代理IP与Socks5代理的技术奇妙之旅

随着数字化时代的崛起&#xff0c;网络工程师们日益承担着维护网络稳定性和保护数据安全的重任。在这个充满挑战的世界里&#xff0c;代理IP与Socks5代理技术成为了他们的秘密武器&#xff0c;本文将带您踏上一段技术奇妙之旅&#xff0c;深入了解这两项技术在不同领域中的应用…...

自动化测试定位不到元素?可能是 frame 在搞鬼

很多人在用Splinter或Selenium定位页面元素的时候会遇到定位不到的问题&#xff0c;明明元素就在那儿&#xff0c;就是定位不到&#xff0c;这种情况很有可能是frame在搞鬼。 说白了就是网站上的网页A&#xff0c;又嵌入了其他网页B。你访问了网页A&#xff0c;在里面可以看到…...

uni-app 开发中,监听 input 键盘事件获取不到按下按键值怎么办?

uniapp 开发 H5 时&#xff0c;无法监听按钮键盘事件的原因以及解决方法。 问题描述&#xff1a; 不少 uni-app 开发者在使用 input 组件时&#xff0c;监听 keyup 事件时&#xff0c;获取不到键盘的 keyCode。编写的代码如下&#xff1a; <template><input keyup&…...

【juc】countdownlatch实现并发网络请求

目录 一、截图示例二、代码示例2.1 测试代码2.2 接口代码 一、截图示例 二、代码示例 2.1 测试代码 package com.learning.countdownlatch;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Arrays; import java.uti…...

在供应链管理中,如何做好库存分析?库存分析有哪些监控指标?

在供应链管理中&#xff0c;库存分析是其重要的一环。库存分析的方法繁杂且广泛&#xff0c;选择正确的方法才能更好的进行库存分析&#xff0c;下面就为大家盘点一些常用的库存分析方法和监控指标&#xff0c;全程干货&#xff0c;建议收藏&#xff01; 01 如何进行库存分析&…...

黑豹程序员-架构师学习路线图-百科:Database数据库

文章目录 1、什么是Database2、发展历史3、数据库排行网4、总结 1、什么是Database 当今世界是一个充满着数据的互联网世界&#xff0c;各处都充斥着大量的数据。即这个互联网世界就是数据世界。 支撑这个数据世界的基石就是数据库&#xff0c;数据库也可以称为数据的仓库。 …...

你相信光吗?黑灯工厂重新相信“光”

你知道“黑灯工厂”吗&#xff1f;望文生义&#xff0c;所谓黑灯工厂&#xff0c;就是可以不需要照明的工厂。全程流水线自动化生产&#xff0c;无人干预、无人值守&#xff0c;工厂变成黑匣子&#xff0c;原材料进去&#xff0c;成品出来&#xff0c;流水线上百分百自动化。 完…...

Ubuntu 20.04使用源码安装nginx 1.14.0

nginx安装及使用&#xff08;详细版&#xff09;是一篇参考博文。 http://nginx.org/download/可以选择下载源码的版本。 sudo wget http://nginx.org/download/nginx-1.14.0.tar.gz下载源代码。 sudo tar xzf nginx-1.14.0.tar.gz进行解压。 cd nginx-1.14.0进入到源代码…...

springboot框架拦截器中HttpServletRequest 请求如何区分是图片上传流还是普通的字符流?

在Spring Boot框架中的拦截器&#xff08;Interceptor&#xff09;中&#xff0c;可以通过检查Content-Type请求头来区分图片上传流和普通的字符流。 当客户端发送POST请求并携带文件时&#xff0c;Content-Type请求头通常会包含multipart/form-data或者类似的值。这表明该请求…...

简单聊聊 TCP 协议

简单聊聊 TCP 协议 如何实现可靠传输 ?完全可靠存在比特差错存在丢包流水线可靠数据传输协议回退N步 (GBN)选择重传 (ARQ) 小结 TCPTCP 连接报文段结构序号和确认号 可靠数据传输避免重传超时时间加倍快速重传回退N步还是选择重传 流量控制连接管理拥塞控制拥塞原因拥塞控制方…...

钡铼BL124PN:简单快速转换Profinet到Ethernet/IP

钡铼技术BL124PN是一款高性能的Profinet转Ethernet/IP网关设备。该网关专为工业自动化领域设计&#xff0c;用于实现不同协议之间的互连和通信。BL124PN采用可靠稳定的硬件和先进的通信技术&#xff0c;具有以下主要特点&#xff1a; 协议转换能力&#xff1a;BL124PN能够将Pr…...

【golang】go 空结构体 详解 空结构体内容占用及大小

一、空结构体基础 空结构实例 和 空结构体变量 本质是一样的 1、所有空结构体地址都是一样的2、大小都为0&#xff08;最独特的&#xff09; package mainimport ("fmt""time""unsafe" )type EST struct { }func main() {// 一、基础// 空结构…...

身为产品经理该如何向客户推广API商品数据接口

在当今数字化的时代&#xff0c;API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;已成为各种软件应用程序之间交互数据的主要方式。API商品数据接口作为一种特殊类型的API&#xff0c;能够让不同的系统之间共享商品数据&#xff0c;…...

【数据结构】460. LFU 缓存

460. LFU 缓存 解题思路 get操作 返回key对应的val 然后增加对应的freq插入操作 如果key已经存在 直接进行更新 如果不存在 但是容器已经满了 直接进行删除freq最小的Key 之后进行插入 class LFUCache {// key到 val的映射 KVHashMap<Integer,Integer> keyToVal;// …...

文字转语音播报模块(一):阿里云nls服务使用示例

一、业务场景 最近笔者在业务中涉及到语音告警的模块&#xff0c;需要讲告警内容以文件或流形式返回给前端进行语音播报&#xff0c;具体的分析与处理如下 二、业务分析 首先告警内容提示信息这里做的处理是通过专门字段去存储、编辑&#xff0c;根据拟定好的代码逻辑判断是…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)

题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

【java面试】微服务篇

【java面试】微服务篇 一、总体框架二、Springcloud&#xff08;一&#xff09;Springcloud五大组件&#xff08;二&#xff09;服务注册和发现1、Eureka2、Nacos &#xff08;三&#xff09;负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...

react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架

1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...