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

Spring Cloud Gateway 实战:从网关搭建到过滤器与跨域解决方案

Spring Cloud Gateway 实战:从网关搭建到过滤器与跨域解决方案

  • 一 . 为什么需要网关 ?
    • 1.1 网关的介绍
    • 1.2 网关的技术实现
    • 1.3 小结
  • 二 . 搭建网关服务
    • 2.1 创建 gateway 模块 , 引入依赖
    • 2.2 编写基础配置和路由规则
    • 2.3 重启测试
    • 2.4 小结
  • 三 . 路由断言工厂 (Route Predicate Factory)
    • 3.1 定义
    • 3.2 具体的断言工厂
    • 3.3 小结
  • 四 . 过滤器工厂
    • 4.1 路由过滤器的种类
    • 4.2 请求头过滤器
    • 4.3 默认过滤器
    • 4.4 自定义全局过滤器
    • 4.5 过滤器的执行顺序
    • 4.6 小结
  • 五 . 跨域问题
    • 5.1 什么是跨域问题 ?
    • 5.2 模拟跨域问题
    • 5.3 解决跨域问题

在微服务架构中,网关作为系统的统一入口,承担着路由分发、权限校验、流量控制等核心功能。本文将深入讲解 Spring Cloud Gateway 的核心应用,从为什么需要网关开始,逐步介绍网关服务搭建、路由断言配置、过滤器工厂使用及跨域问题解决方案。内容涵盖 Gateway 基础路由规则、多种断言工厂的实际应用、请求过滤与全局拦截实现,以及跨域资源访问的完整解决方案,帮助读者构建安全、高效的微服务网关层。
在这里插入图片描述

本专栏的内容均来自于 B 站 UP 主黑马程序员的教学视频,感谢你们提供了优质的学习资料,让编程不再难懂。
专栏地址 : https://blog.csdn.net/m0_53117341/category_12835102.html

一 . 为什么需要网关 ?

1.1 网关的介绍

网关的介绍.png

那网关的功能如下 :

  1. 身份认证和权限校验 : 网关作为微服务的入口 , 需要校验用户是否具有请求资格 , 如果没有则需要进行拦截
  2. 服务路由、负载均衡 : 一切请求都需要先经过 gateway , 但是网关不处理任何业务 , 而是根据某种规则 , 把请求转发到某个微服务 , 这个过程叫做路由 . 当路由的目标服务存在多个时 , 还需要进行负载均衡策略 .
  3. 请求限流 : 当请求的流量过高 , 就会在网关中按照下流方的微服务所能够接受的速度来放行请求 , 避免服务压力过大

1.2 网关的技术实现

在 Spring Cloud 中网关的实现主要包括两种 :

  1. gateway
  2. zuul

那 zuul 是基于 Servlet 的实现 , 属于阻塞式编程 . 而 SpringCloudGateway 则是基于 Spring5 来实现的 , 属于响应式编程的实现 , 具备更好的性能 .

1.3 小结

网关的作用 :

  1. 对用户请求做身份验证、权限校验
  2. 将用户请求路由到微服务 , 并且实现负载均衡
  3. 对用户请求做限流

二 . 搭建网关服务

2.1 创建 gateway 模块 , 引入依赖

首先需要我们创建一个新的 module

然后引入 SpringCloudGateway 的依赖和 Nacos 的服务发现依赖

因为网关也需要拉取健康的服务列表 , 所以还需要引入 Nacos 的依赖

<!-- Gateway 网关 -->
<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>

然后将该模块的启动类以及配置文件创建出来

2.2 编写基础配置和路由规则

接下来打开 yml 配置文件 , 填写下面的配置

server:port: 10010 # 网关端口
spring:application:name: gateway # 给当前微服务起一个名称cloud:nacos:username: nacos # Nacos 的账号password: nacos # Nacos 的密码discovery:server-addr: localhost:8848 # Nacos 地址gateway:routes: # 网关路由配置- id: user-service # 当前路由 ID, 自定义保持唯一即可uri: lb://userservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/user/**

我们也可以通过源码来看一下 routes 需要填写哪些参数

我们目前先关心这三个信息

其中的这个 uri , 如果我们访问单独的某个机器 , 直接设置成具体的 URL 即可

uri: http://127.0.0.1:8081 # 路由的目标地址, http 就是固定地址

但是我们如果通过网关 , 就只能一直使用这个端口对应的机器 , 肯定是不可以的 .

那我们就可以使用这种方式 lb://userservice , 它指的是负载均衡的访问 userservice 这个服务 .

然后下面还指定了一些条件, - Path=/user/** 指的是所有以 /user 开头的路径 , 都会以负载均衡的方式访问 user-service 服务 . 如果想要指定多个拦截规则的话 , 在后面继续指定即可 , 比如 :

- Path=/user/**,/user2/**

/** 代表多级路径 , 也就是以 /user 开头的 URL 就满足规则 , 比如 : /user/id/1

/* 代表一级路径 , 只能满足 /user 后面只有一级的情况 , 比如 : /user/1

那如果有多个网关路由的配置 , 在后面继续添加即可

server:port: 10010 # 网关端口
spring:application:name: gateway # 给当前微服务起一个名称cloud:nacos:username: nacos # Nacos 的账号password: nacos # Nacos 的密码discovery:server-addr: localhost:8848 # Nacos 地址gateway:routes: # 网关路由配置- id: user-service # 当前路由 ID, 自定义保持唯一即可uri: lb://userservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/user/**- id: order-service # 当前路由 ID, 自定义保持唯一即可uri: lb://orderservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/order/**

2.3 重启测试

接下来 , 我们运行 GatewayApp , 来看一下效果

访问 http://127.0.0.1:10010/order/101

http://127.0.0.1:10010/user/1

2.4 小结

网关搭建步骤 :

  1. 创建项目 , 引入 Nacos 服务发现和 Gateway 依赖
  2. 配置 application.yml , 包括服务基本信息、Nacos 地址、路由

路由配置包括 :

  1. 路由 id : 路由的唯一标识
  2. 路由目标 (uri) : 路由的目标地址 , HTTP 代表固定地址 , lb 代表根据服务名称进行负载均衡策略
  3. 路由断言 (predicates) : 判断路由的规则
  4. 路由过滤器 (filters) : 对请求或相应做处理

三 . 路由断言工厂 (Route Predicate Factory)

我们之前已经完成了搭建网关服务 , 那需要以下配置 :

  1. 路由 id : 路由的唯一标识
  2. uri : 路由的目的地 , 支持 lb 和 HTTP 两种
  3. predicates : 路由断言 , 判断请求是否符合要求 , 符合则转发到路由目的地
  4. filters : 路由过滤器 , 处理请求或响应

那其中的第三点 : 路由断言 , 就是我们接下来需要讨论的问题

3.1 定义

我们在配置文件中写的断言规则只是字符串 , 那这些字符串会被 Predicate Factory 读取并且处理 , 转变为路由判断的条件 .

比如 : - Path = /user/** 就是按照路径匹配 , 那这个规则就是由 Predicate Factory 来处理的

3.2 具体的断言工厂

名称说明示例
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或者- Query=name
RemoteAddr请求者的 ip 必须是指定范围- RemoteAddr=192.168.1.1/24
Weight权重处理

3.3 小结

❓ RoutePredicateFactory (路由断言工厂) 的作用是什么 ?

✔️ 根据请求的参数进行条件判断 , 成立的话就进行放行 . 不成立的话 , 就会返回 404

❓ Path=/user/** 是什么含义 ?

✔️ /user 开头的任意路径 , 只要符合条件就进行放行

四 . 过滤器工厂

GatewayFilter 是网关中提供的一种过滤器 , 可以对进入网关的请求和微服务所返回的响应进行处理 , 如图所示 :

4.1 路由过滤器的种类

Spring 提供了共 31 种不同的路由过滤器工厂 , 我们介绍几个比较常见的

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除一个响应头
RequestRateLimiter限制请求的流量

4.2 请求头过滤器

需求 : 给所有经过 user-service 服务的请求添加一个请求头 : Hello World

实现方式 : 在 Gateway 中修改 application.yml 文件 , 给 userservice 的路由添加过滤器

server:port: 10010 # 网关端口
spring:application:name: gateway # 给当前微服务起一个名称cloud:nacos:username: nacos # Nacos 的账号password: nacos # Nacos 的密码discovery:server-addr: localhost:8848 # Nacos 地址gateway:routes: # 网关路由配置- id: user-service # 当前路由 ID, 自定义保持唯一即可uri: lb://userservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/user/**filters: # 过滤器- AddRequestHeader=key, Hello World! # , 前半部分是 key, 后半部分是内容- id: order-service # 当前路由 ID, 自定义保持唯一即可uri: lb://orderservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/order/**

接下来 , 我们就可以编写一个方法来进行测试了

那我们需要在参数中添加 @RequestHeader 注解来去获取请求头中的内容

除此之外 , 我们还需要额外指定一个属性

所以我们还需要添加 required 属性 , 设置为 false

required = true(默认值) :
若请求中缺少该请求头(如key不存在) , 则会抛出 400 Bad Request 异常 , 导致请求失败

required = false :
若请求中缺少该请求头 , 参数值会被自动设为null , 不会触发异常

package com.example.user.web;import com.example.user.pojo.User;
import com.example.user.service.UserService;
import com.example.user.vo.DateFormatVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;@Slf4j
@RestController
// @RefreshScope
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/testHeader")public String testHeader(@RequestHeader(value = "key", required = false) String header) { // 在参数中添加注解 @RequestHeader 来去获取请求头中的内容return header;}
}

我们重启 user-service 服务和 gateway-server 服务

然后访问 http://127.0.0.1:10010/user/testHeader

4.3 默认过滤器

如果想要对所有的路由都生效 , 则可以将过滤器工厂写到 default-filters 下面

server:port: 10010 # 网关端口
spring:application:name: gateway # 给当前微服务起一个名称cloud:nacos:username: nacos # Nacos 的账号password: nacos # Nacos 的密码discovery:server-addr: localhost:8848 # Nacos 地址gateway:routes: # 网关路由配置- id: user-service # 当前路由 ID, 自定义保持唯一即可uri: lb://userservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/user/**- id: order-service # 当前路由 ID, 自定义保持唯一即可uri: lb://orderservice # 路由的目标地址, lb 表示负载均衡predicates: # 路由断言, 判断请求是否符合路由规则的条件- Path=/order/**default-filters: # 全局过滤器- AddRequestHeader=key, Hey World! # , 前半部分是 key, 后半部分是内容

那我们可以重启一下网关服务 , 看一下效果

访问 http://127.0.0.1:10010/user/testHeader

4.4 自定义全局过滤器

全局过滤器 , 其实就是自定义过滤器 . 它的作用也是对一切进入网关的请求和微服务的响应进行处理的 , 与 GatewayFilter 的作用一致 .

但是之前学习的过滤器 , 它的作用都是固定的 . 如果我们希望拦截请求 , 然后实现自己的业务逻辑 , 那 Spring 提供给我们的 31 种过滤器则无法实现 .

那接下来我们就给大家讲一下自定义全局过滤器的实现步骤

需求 : 自定义一个全局过滤器 , 拦截请求 , 判断请求的参数中是否满足以下条件

  • 参数中是否有 keyword
  • keyword 参数值是否为 helloworld

如果同时满足则放行 , 否则进行拦截 , 并返回 403 状态码给用户 .

第一步 : 创建一个全局过滤器类

第二步 : 实现 GlobalFilter 接口 , 重写他的 filter 方法

其中 , 这个方法提供了两个参数

  • ServerWebExchange exchange : 封装了请求的上下文信息, 包含: request、response
  • GatewayFilterChain chain : 过滤器链

第三步 : 添加 @Component 注解 , 将全局过滤器添加到容器中 . 添加 @Order(-1) 注解表示优先执行

数字越小 , 执行越靠前

第四步 : 编写校验规则

package com.example.gatway.filter;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
@Order(-1) // 数字越小, 执行越靠前
public class AuthorizeFilter implements GlobalFilter {/*** 过滤规则方法实现** @param exchange 封装了请求的上下文信息, 包含: request response* @param chain    过滤器链* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1. 获取 URL 的参数// e.g. http://127.0.0.1:8080/testHeader?name=zhangsan// 获取的是 ? 后面的内容MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();// 建议通过 var 的方式直接生成返回值类型// 2. 获取 ? 后面的第一个参数String keyword = queryParams.getFirst("keyword");// 3. 设置条件if (keyword.equals("helloworld")) {// 满足条件则放行return chain.filter(exchange);}// 4. 不满足规则, 需要设置状态码 403exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// 5. 返回 setComplete 方法就代表过滤器链结束return exchange.getResponse().setComplete();}
}

我们可以重启一下 gateway-server 服务 , 看一下效果

不带 value 的情况 : http://127.0.0.1:10010/user/testHeader?keyword=

带 value 的情况 : http://127.0.0.1:10010/user/testHeader?keyword=helloworld

4.5 过滤器的执行顺序

当请求进入到网关 , 会碰到三类过滤器 : 当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由之后 , 会将这三类过滤器 , 合并到一个过滤器链 (集合) 中 , 排序后依次执行每个过滤器

过滤器的执行顺序

  1. 每个过滤器都必须指定一个 int 类型的 order 值 , order 值越小 , 优先级越高 , 执行顺序越靠前 .
  2. GlobalFilter 通过添加 @Order 注解来指定 order 的值
  3. 路由过滤器和 defaultFilter 的 order 由 Spring 指定 , 默认是按照声明顺序从 1 递增
  4. 当过滤器的 order 值一样的时候 , 会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序去执行 .

4.6 小结

过滤器的作用是什么 ?

  1. 对路由的请求或响应做加工处理 , 比如添加请求头
  2. 配置在路由下的过滤器只对当前路由的请求生效

defaultFilters 的作用是什么 ?

  • 对所有路由都生效的过滤器

全局过滤器的作用是什么 ?

  • 对所有路由都生效的过滤器 , 并且可以自定义处理逻辑

实现全局过滤器的步骤

  1. 添加 @Component 注解将全局过滤器注册到 Spring 中
  2. 实现 GlobalFilter 接口 , 重写 filter 方法
  3. 添加 @Order(-1) 注解
  4. 编写处理逻辑

路由过滤器、defaultFilter、全局过滤器的执行顺序

  1. order 值越小 , 优先级越高
  2. 当 order 的值一样时 , defaultFilter > 局部的路由过滤器 > 全局过滤器

五 . 跨域问题

5.1 什么是跨域问题 ?

跨域问题就是浏览器禁止请求的发起者与服务端发生跨域 ajax 请求 , 请求被浏览器拦截的问题

  • 域名不同 : 比如 www.taobao.com 和 www.taobao.org
  • 端口不同 : localhost:8080 和 localhost:8081
  • 协议不同 : 一个是 HTTPS , 一个是 HTTP

5.2 模拟跨域问题

我们先将需要用到的测试文件发给大家

index.html

我们可以看一下里面的内容

然后我们将这个文件替换掉 nginx 内部默认的 index.html

那这时候 , 就出现跨域问题了 , 我们的 index.html 要访问 10010 端口 , 但是它所对应的 nginx 端口号是 80 , 这就是域名相同但是端口号不同的情况 .

那我们就可以启动 nginx 服务 , 来看一下效果

浏览器直接访问 localhost : http://localhost/

5.3 解决跨域问题

在 gateway-server 的 application.yml 中进行配置即可

spring:cloud:gateway:globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决 options 请求 (非 get、post 方法) 被拦截问题corsConfigurations:'[/**]':allowedOrigins: # 允许哪些网站的跨域请求- "http://localhost:8090"- "http://localhost"  # 80 端口要省略不写- "http://127.0.0.1"allowedMethods: # 允许的跨域 ajax 的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息, * 代表请求头包含什么信息都可以allowCredentials: true # 是否允许携带 cookiemaxAge: 360000 # 这次跨域检测的有效期 - 避免频繁发起跨域检测, 服务端返回 Access-Control-Max-Age 来声明的有效期

注意 : globalcors 要与 routes 或者 default-filters 同级

此时我们重启网关服务


我们还可以通过 @CrossOrigin 注解让当前类解决跨域问题


小结 :

  1. 网关核心概念与作用

    1. 核心功能:身份认证、服务路由、负载均衡、请求限流,作为微服务统一入口拦截和转发请求。
    2. 技术选型:Spring Cloud 中主流网关实现为 Gateway(响应式编程)和 Zuul(阻塞式),Gateway 性能更优。
  2. 网关服务搭建与路由配置

    1. 搭建步骤:创建网关模块,引入 Gateway 和 Nacos 依赖,配置端口、服务名、Nacos 地址及路由规则。

    2. 路由要素:

      • id:路由唯一标识;
      • uri:目标地址(lb://服务名实现负载均衡);
      • predicates:路由断言(如 Path、Method、Header 等条件匹配)。
  3. 路由断言与过滤器工厂

    1. 断言工厂:通过规则字符串(如 Path=/user/**)判断请求是否符合路由条件,支持时间、路径、参数等多种断言类型。

    2. 过滤器工厂:

      • 路由过滤器:针对单个路由生效,如 AddRequestHeader 添加请求头;
      • 默认过滤器(default-filters):对所有路由生效;
      • 全局过滤器:自定义拦截逻辑,需实现 GlobalFilter 接口,通过 @Order 控制执行顺序。
  4. 跨域问题解决方案

    1. 跨域定义:域名、端口、协议不同导致的浏览器请求拦截问题。
    2. Gateway 配置:通过 globalcors 配置允许的域名、请求方式、头信息等,解决跨域访问限制。

相关文章:

  • 应用层网络编程范式
  • NLP文本增强——随机删除
  • webpack+vite前端构建工具 - 10 开发模式
  • Vue2 第四节 脚手架的安装及目录说明
  • RabbitMQ面试题汇总
  • [面试] 手写题-数组转树
  • CNN, RNN, LSTM
  • AI智能体|扣子(Coze)搭建【沉浸式历史故事解说视频】工作流
  • 【unity游戏开发——网络】IP和端口相关的类——IPAddress和IPEndPoint
  • Java 中通信接口
  • [Linux] PXE
  • 各种常用的串口助手工具分享
  • Python爬虫-爬取汽车之家全部汽车品牌及车型数据
  • 【系统分析师】高分论文:论软件测试方法和工具的选择
  • 量子算法入门——5.Qiskit库介绍与简单应用(2)
  • React与Vue的主要区别
  • 数据结构复习4
  • AWTK-MVVM的一些使用技巧总结(1)
  • 爪形行列式
  • 建造者模式 - Flutter中的乐高大师,优雅组装复杂UI组件!