【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 的结果了:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/99043.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring的事务控制

目录 基于AOP的声明事务控制 Spring事务编程概述 搭建测试环境 基于xml声明式事务控制 详解 事务增强的AOP 平台事务管理器 Spring提供的Advice&#xff08;重点介绍&#xff09; 原理 &#xff08;源码没有翻太明白&#xff09; 基于注解声明式事务控制 基于AOP的声明…

小视频APP源码选择指南:挑选最适合你的开发框架

在如今蓬勃发展的小视频APP行业中&#xff0c;源码的选择是打造一款成功应用的关键步骤。然而&#xff0c;面对众多开发框架的选择&#xff0c;如何挑选最适合你的小视频APP源码呢&#xff1f;作为这一领域的专家&#xff0c;我将为你提供一份详尽的指南&#xff0c;助你在源码…

Windows10打开应用总是会弹出提示窗口的解决方法

用户们在Windows10电脑中打开应用程序&#xff0c;遇到了总是会弹出提示窗口的烦人问题。这样的情况会干扰到用户的正常操作&#xff0c;给用户带来不好的操作体验&#xff0c;接下来小编给大家详细介绍关闭这个提示窗口的方法&#xff0c;让大家可以在Windows10电脑中舒心操作…

智能工厂MES系统,终端设备支持手机、PDA、工业平板、PC

一、开源项目简介 源计划智能工厂MES系统(开源版) 功能包括销售管理&#xff0c;仓库管理&#xff0c;生产管理&#xff0c;质量管理&#xff0c;设备管理&#xff0c;条码追溯&#xff0c;财务管理&#xff0c;系统集成&#xff0c;移动端APP。 二、开源协议 使用GPL-3.0开…

010:连续跌3天,同时这三天收盘价都在20日均线下,第四天上涨的概率--以京泉华为例

对于《连续跌三天&#xff0c;压第四天上涨的盈利计算》&#xff0c;我们可以继续优化这个策略&#xff0c;增加条件&#xff1a;同时三天都收盘在20日均线下。 因为我们上一篇《获取20日均线数据到excel表中》获得了20日均线数据&#xff0c;我们可以利用均线数据来编写新的脚…

基于SpringBooy的安康旅游网站的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 登录模块的实现 景点信息管理界面 酒店信息管理界面 特产管理界面 游客管理界面 景点购票订单管理界面 系统主界面 游客注册界面 景点信息详情界面 酒店详情界面 特产详情界面 三、核心代码 1、登录模块 2、文件上传模块…

c++桥接模式,中介者模式应用实现状态跳转

上图为例&#xff0c;按上述两种方式实现的模式跳转&#xff0c;如果在原先的三种模式之间再增加多一种模式&#xff0c;就会引起每个模式都会要求改变&#xff0c;并且逻辑混乱&#xff0c;因此更改模式为桥接中介者访问&#xff0c;将抽象和实现分离&#xff0c;实现之间采用…

Ultra-Fast-Lane-Detection-v2 {后处理优化}//参考

采用三次多项式拟合生成的anchor特征点&#xff0c;在给定的polyfit_draw函数中&#xff0c;degree参数代表了拟合多项式的度数。 具体来说&#xff0c;当我们使用np.polyfit函数进行数据点的多项式拟合时&#xff0c;我们需要指定一个度数。这个度数决定了多项式的复杂度。例…

JProfiler14.0(Java开发分析)

JProfiler是一款专业的Java应用程序性能分析工具&#xff0c;可帮助开发人员识别和解决Java应用程序中的性能问题。JProfiler支持Java SE、Java EE和Android平台&#xff0c;提供了多种分析选项&#xff0c;包括CPU分析、内存分析和线程分析等。 使用JProfiler&#xff0c;开发…

【设计模式】使用原型模式完成业务中“各种O”的转换

文章目录 1.原型模式概述2.浅拷贝与深拷贝2.1.浅拷贝的实现方式2.2.深拷贝的实现方式 3.结语 1.原型模式概述 原型模式是一种非常简单易懂的模型&#xff0c;在书上的定义是这样的&#xff1a; Specify the kinds of objects to create using a prototypical instance,and cre…

C语言打印菱形

一、运行结果图 二、源代码 # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int line 0;int i 0;int j 0;//获取变量值&#xff1b;scanf("%d", &line);//循环打印上半部分&#xff1b;for (i 0; i <…

Spring在业务中常见的使用方式

目录 通过IOC实现策略模式 通过AOP实现拦截 通过Event异步解耦 通过Spring管理事务 通过IOC实现策略模式 很多时候&#xff0c;我们需要对不同的场景进行不同的业务逻辑处理举个例子&#xff0c;譬如不同的场景需要不同支付方式&#xff0c;普通的逻辑是使用if-else&#x…

阿里云对象存储OSS SDK的使用

官方文档 https://help.aliyun.com/zh/oss/developer-reference/java 准备工作 windows安装好JDK&#xff0c;这里使用JDK1.8为例 windows安装好IDEA&#xff0c;这里使用IDEA2022 登录阿里云控制台&#xff0c;通过免费试用OSS或开通OSS 步骤 配置访问凭证 有临时和长期…

文化主题公园旅游景点3d全景VR交互体验加深了他们对历史文化的认知和印象

如今&#xff0c;沉浸式体验被广泛应用于文旅行业&#xff0c;尤其是在旅游演艺活动中。在许多城市&#xff0c;沉浸式旅游演艺活动已成为游客“必打卡”项目之一。因其独特体验和强互动性&#xff0c;这类演艺活动不仅吸引了外地游客&#xff0c;也吸引了本地观众。 随着信息化…

解决: 使用html2canvas和print-js打印组件时, 超出高度出现空白页

如果所示&#xff1a;当我利用html2canvas转换成图片后, 然后使用print-js打印多张图片, 第一张会出现空白页 打印组件可参考这个: Vue-使用html2canvas和print-js打印组件 解决: 因为是使用html2canvas转换成图片后才打印的, 而图片是行内块级元素, 会有间隙, 所以被挤下去了…

大数据——Spark Streaming

是什么 Spark Streaming是一个可扩展、高吞吐、具有容错性的流式计算框架。 之前我们接触的spark-core和spark-sql都是离线批处理任务&#xff0c;每天定时处理数据&#xff0c;对于数据的实时性要求不高&#xff0c;一般都是T1的。但在企业任务中存在很多的实时性的任务需求&…

Ubuntu22.04.3安装教程

虚拟机系列文章 VMware Workstation Player 17 免费下载安装教程 VMware Workstation 17 Pro 免费下载安装教程 windows server 2012安装教程 Ubuntu22.04.3安装教程 FTP服务器搭建 Ubuntu22.04.3安装教程 虚拟机系列文章前言Ubuntu22.04.3安装&#xff08;图文&#xff09; 前…

Linux系列---【查看mac地址】

查看mac地址命令 查看所有网卡命令 nmcli connection show 查看物理网卡mac地址 ifconfig 删除网卡 nmcli connection delete virbr0 禁用libvirtd.service systemctl disable libvirtd.service 启用libvirtd.service systemctl enable libvirtd.service

软件工程与计算总结(五)软件需求基础

本帖介绍软件需求涉及的诸多基本概念&#xff0c;通过对这些概念的阐述&#xff0c;剖析软件需求的来源、层次、类别、作用等重要知识~ 目录 ​编辑 一.引言 二.需求工程基础 1.简介 2.活动 3.需求获取 4.需求分析 5.需求规格说明 6.需求验证 7.需求管理 三.需求基…

【动手学深度学习】课程笔记 00-03 深度学习介绍及环境配置

目录 00-01 课程安排 02 深度学习介绍 深度学习实际应用的流程 完整的故事 03 环境配置 00-01 课程安排 1. 学习了这门课&#xff0c;你将收获什么&#xff1f; 深度学习的经典和最新模型&#xff1a;LeNet&#xff0c;ResNet&#xff0c;LSTM&#xff0c;BERT&#xff1…