Lecture07-SpringCloud-网关Gateway

文章目录

    [toc]

    单词词性音标含义
    routen./v./ruːt/路由(把请求按规则转发到对应的下游服务)
    predicaten./ˈpredɪkeɪt/谓词(路由匹配条件:路径、时间、请求头、参数等)
    filtern./v./ˈfɪltə(r)/过滤器(在请求转发前后做处理:鉴权、日志、改写、跨域)
    cross-originadj./krɒs ˈɒrɪdʒɪn/跨域(浏览器对跨站请求的限制,网关可统一处理)
    fallbackn./ˈfɔːlbæk/兜底(请求匹配不到路由时的处理)

    Lecture07-SpringCloud-网关Gateway

    目标:理解网关"统一入口"的职责,掌握 Spring Cloud Gateway 的路由、谓词、过滤器配置,并实现统一鉴权与跨域。

    1、为什么需要网关

    微服务有几十个服务、几百个接口,如果前端直接调各服务:地址散落、权限散落、不利于统一治理。网关作为所有请求的统一入口,负责:

    • 路由转发:按规则把请求转到对应服务。
    • 统一鉴权:所有请求先过网关验 token,后端服务不用重复写。
    • 统一限流、日志、跨域:集中处理(限流见 L09 概念)。
    客户端 → 网关 Gateway → user-service / order-service / product-service …
    

    gateway-arch

    一句话:网关 = 微服务的"大门 + 门卫"。前端只认这一个地址。


    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 的两个破坏性变更(重要)

    1. 依赖坐标改名:spring-cloud-starter-gateway → spring-cloud-starter-gateway-server-webflux。旧名字在 2025.0 已标记废弃,2025.1 直接从 BOM 删除——写旧名 Maven 报 Dependency ... not found。
    2. 配置前缀改名:下面 yaml 里的 spring.cloud.gateway.* 改成 spring.cloud.gateway.server.webflux.*。

    改名原因:Spring 把网关按"服务器模式 / 代理交换模式" × "WebFlux / WebMVC" 四种组合拆开了。本课程用的是最经典的"服务器模式 + WebFlux"组合。

    1. 网关也要显式加 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、课堂练习

    1. 新建 gateway-service,配置 user-route、order-route 两条路由。
    2. 通过网关 80 端口访问 /user/1、/order/user/2(即 http://localhost/user/1),验证成功转发。
    3. 写 AuthGlobalFilter:不带 token 返回 401,带则放行。
    4. (挑战)给路由加 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)✅
    end