[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| route | n./v. | /ruːt/ | 路由(把请求按规则转发到对应的下游服务) |
| predicate | n. | /ˈpredɪkeɪt/ | 谓词(路由匹配条件:路径、时间、请求头、参数等) |
| filter | n./v. | /ˈfɪltə(r)/ | 过滤器(在请求转发前后做处理:鉴权、日志、改写、跨域) |
| cross-origin | adj. | /krɒs ˈɒrɪdʒɪn/ | 跨域(浏览器对跨站请求的限制,网关可统一处理) |
| fallback | n. | /ˈfɔːlbæk/ | 兜底(请求匹配不到路由时的处理) |
Lecture07-SpringCloud-网关Gateway
目标:理解网关"统一入口"的职责,掌握 Spring Cloud Gateway 的路由、谓词、过滤器配置,并实现统一鉴权与跨域。
1、为什么需要网关
微服务有几十个服务、几百个接口,如果前端直接调各服务:地址散落、权限散落、不利于统一治理。网关作为所有请求的统一入口,负责:
- 路由转发:按规则把请求转到对应服务。
- 统一鉴权:所有请求先过网关验 token,后端服务不用重复写。
- 统一限流、日志、跨域:集中处理(限流见 L09 概念)。
客户端 → 网关 Gateway → user-service / order-service / product-service …
一句话:网关 = 微服务的"大门 + 门卫"。前端只认这一个地址。
2、核心概念:路由 / 谓词 / 过滤器
| 概念 | 作用 | 通俗理解 |
|---|---|---|
| Route 路由 | 一条转发规则:匹配条件 + 目标地址 | "哪些请求去哪个服务" |
| Predicate 谓词 | 路由的匹配条件(基于路径/时间/请求头等) | "这个请求符合条件吗" |
| Filter 过滤器 | 请求转发前/后的加工(鉴权、日志、改写、跨域) | "转发前后做点什么" |
一条请求的完整路径:谓词命中路由 → 过滤器加工 → 转发到下游 → 响应再经过滤器返回。
3、接入网关(gateway-service)
注意:请一定要单独新建一个 module(微服务)来作为网关,网关和具体业务逻辑所在的微服务千万不要放在一起!!!
- 从技术角度来说:网关基于 Webflux 是响应式编程(流失编程)技术体系和传统 SpringMVC Web 开发技术格格不入
- 从项目架构来说:网关和具体业务微服务各司其职,功能不同
1、加依赖
<!-- gateway-service/pom.xml -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>
<!-- 网关也要注册到 Nacos,才能按服务名路由 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- ⚠️ 网关用 lb:// 按服务名转发,必须自己加 loadbalancer(gateway 不传递它) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
注意:网关是 WebFlux(响应式),不要引入
spring-boot-starter-web,否则冲突。
⚠️ Spring Cloud 2025.1 的两个破坏性变更(重要)
- 依赖坐标改名:
spring-cloud-starter-gateway→spring-cloud-starter-gateway-server-webflux。旧名字在 2025.0 已标记废弃,2025.1 直接从 BOM 删除——写旧名 Maven 报Dependency ... not found。- 配置前缀改名:下面 yaml 里的
spring.cloud.gateway.*改成spring.cloud.gateway.server.webflux.*。改名原因:Spring 把网关按"服务器模式 / 代理交换模式" × "WebFlux / WebMVC" 四种组合拆开了。本课程用的是最经典的"服务器模式 + WebFlux"组合。
- 网关也要显式加 loadbalancer:
uri: lb://user-service这个写法靠spring-cloud-starter-loadbalancer把"服务名"解析成"实例 IP"。gateway 不依赖传递它,缺了它请求会 503 Service Unavailable(路由能匹配,但转不出去)。
- 破坏性更新:底层更新后会导致上层代码跟着改变
- 新版本向下兼容:底层更新后上层代码不变
2、配置路由
server:
port: 80 # HTTP 默认端口,前端访问时不用带端口号
spring:
application:
name: gateway-service
cloud:
nacos:
server-addr: 192.168.100.100:8848
gateway:
server:
webflux: # 2025.1 起配置前缀改了
routes:
- id: user-route # 路由唯一标识
uri: lb://user-service # 目标:按服务名负载均衡到 user-service
predicates:
- Path=/user/** # 请求路径以 /user/ 开头 → 命中
- id: order-route
uri: lb://order-service
predicates:
- Path=/order/**
关键写法:
uri: lb://服务名表示"通过 LoadBalancer 到 Nacos 里的该服务",不用写具体 IP。
哪些服务要配路由?
不是所有微服务都要进网关路由表。判断标准很简单:前端是否直接访问它。
浏览器 ──> 网关:80 ──> order-service:8083
│
└─Feign─> user-service:8081(前端不直接访问)
└─Feign─> product-service:8082(前端不直接访问)
| 服务 | 配网关路由? | 原因 |
|---|---|---|
| order-service | ✅ 配 | 前端通过网关调 /order/create |
| user-service | ❌ 不配 | order-service 通过 Feign 内部调用,前端不直接访问 |
| product-service | ❌ 不配 | 同上 |
注意:不配路由 ≠ 不注册到 Nacos。user/product 仍然要注册(否则 order 的 Feign 找不到它们),只是网关不需要知道它们的存在。
好处:user/product 的端口甚至可以不对公网开放,只在内网监听。网关是唯一对外入口,攻击面更小。
⚠️ 80 端口的权限坑(重要)
Linux 下非 root 用户不能绑定 1024 以下的端口,80 就在其中。用普通用户启动 gateway-service 会报权限错误。推荐做法:给 JDK 的 java 授予绑定低位端口的能力,之后正常启动即可。
sudo setcap 'cap_net_bind_service=+ep' $(readlink -f $(which java))
说明:换 JDK 版本后需要重新执行一次。若不想动权限,也可以把网关卡改回 8080,但要同步给 Nacos 控制台换端口(控制台默认占用 8080)。
3、启动测试
启动 gateway-service 后:
curl http://localhost/user/1 # 网关把请求转到 user-service 的 /user/1
curl http://localhost/order/user/2 # 80 是 HTTP 默认端口,可省略不写
网关端口是 80(HTTP 默认端口),前端只需要访问这一个地址。
4、常用谓词(匹配条件)
| 谓词 | 作用 | 示例 |
|---|---|---|
Path | 按路径匹配 | Path=/user/** |
Method | 按请求方法匹配 | Method=GET,POST |
Header | 按请求头匹配 | Header=X-Request-Id, \d+ |
Query | 按查询参数匹配 | Query=page |
Cookie | 按 Cookie 匹配 | Cookie=token, abc |
After/Before/Between | 按时间匹配 | After=2026-01-01T00:00:00+08:00 |
predicates:
- Path=/user/**
- Method=GET # 该路由只接收 GET
- Query=id # 必须带 id 参数
5、全局过滤器:统一鉴权
用 GlobalFilter 实现"所有请求先验 token":
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token == null || token.isBlank()) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete(); // 401,不放行
}
return chain.filter(exchange); // 校验通过,继续转发
}
}
演示:带
?token=abc能通过,不带则返回 401。真实项目校验 JWT/Redis 里的登录态(衔接 Redis 课)。
6、统一跨域
前后端分离时浏览器会发跨域请求,在网关统一配置即可:
spring:
cloud:
gateway:
server:
webflux:
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
allowed-origin-patterns: "*" # 允许来源(生产按需收紧)
allowed-methods: "*" # 允许方法
allowed-headers: "*" # 允许请求头
allow-credentials: true
7、课堂练习
- 新建
gateway-service,配置user-route、order-route两条路由。 - 通过网关 80 端口访问
/user/1、/order/user/2(即http://localhost/user/1),验证成功转发。 - 写
AuthGlobalFilter:不带token返回 401,带则放行。 - (挑战)给路由加
Method=GET谓词,用 POST 访问观察是否被拦截。
8、本讲小结
- 网关 = 微服务统一入口,前端只认网关地址。
- 三要素:路由(去哪)+ 谓词(什么条件匹配)+ 过滤器(转发前后加工)。
uri: lb://服务名按服务名负载均衡,不写死 IP。- 统一鉴权用
GlobalFilter;跨域用globalcors统一配置。 - Spring Cloud 2025.1 起,gateway 的坐标与配置前缀都改名了(
spring-cloud-starter-gateway-server-webflux/spring.cloud.gateway.server.webflux.*)。
9、本节验证
- 理解网关的作用与 Route / Predicate / Filter 三个核心概念 ✅
- 会搭建 gateway-service(2025.1 用新坐标
spring-cloud-starter-gateway-server-webflux+ 新配置前缀),配置路由转发 ✅ - 会用常用谓词(Path/Method/Header/Query)匹配请求 ✅
- 会写全局过滤器统一鉴权、统一处理跨域 ✅
- 了解 Linux 下绑定 80 端口的权限要求(
setcap)✅