Gateway网关路由、拦截、鉴权、过滤全局控制微服务架构里几十个服务各自暴露端口就像几十扇门同时敞开。网关就是那扇唯一的大门——所有请求都从这里进统一鉴权、统一路由、统一限流。一、API网关的角色微服务的门卫在微服务架构中如果没有网关客户端需要直接调用各个微服务没有网关 客户端 ──→ 设备服务8001 客户端 ──→ 商品服务8002 客户端 ──→ 订单服务8003 客户端 ──→ 支付服务8004这样会暴露出一堆问题服务地址暴露给客户端、没有统一鉴权入口、跨域配置散落各处、无法统一限流和日志。加一个网关后有网关 客户端 ──→ Gateway统一入口:9000 ├── /device/** → 设备服务 ├── /product/** → 商品服务 ├── /order/** → 订单服务 └── /pay/** → 支付服务网关的六大核心职责统一路由转发根据请求路径分发到不同微服务统一鉴权在网关层校验 Token没权限的直接挡回去统一限流配合 Sentinel 在网关层做全局限流统一日志记录所有请求的访问日志跨域处理集中配置 CORS不用每个服务单独配灰度发布通过请求头/Header 控制流量路由到新版本二、Gateway vs Zuul为什么选 Gateway特性Zuul 1.xSpring Cloud Gateway架构模型同步阻塞Servlet响应式异步Netty Reactor性能一般显著优于 ZuulSpring 生态支持逐渐边缘化Spring 官方亲儿子长连接/WebSocket支持差原生支持过滤器机制阻塞式非阻塞式Zuul 2.x 虽然也转了异步模型但 Spring Cloud 社区已经选择了 Gateway 作为官方网关方案Zuul 基本告别历史舞台。Gateway 基于 Spring WebFlux Reactor Netty 构建天生响应式。这意味着它不占用线程等待每个请求完成而是用事件循环处理高并发在同样的机器资源下吞吐量远超 Zuul。坑点提醒Gateway 基于 WebFlux不能和传统 Servlet 容器Tomcat混用。如果你在 Gateway 工程里引入了spring-boot-starter-web启动直接报错。三、Gateway 核心概念Route / Predicate / Filter理解这三个概念Gateway 就学会了 80%。┌─────────────────────────────────────────────────────┐ │ Route路由 │ │ 组成ID URI Predicate Filter │ │ 作用定义一条路由规则请求从哪来到哪去 │ ├─────────────────────────────────────────────────────┤ │ Predicate断言 │ │ 作用判断请求是否匹配某条路由 │ │ 比如Path/product/** 匹配 /product/ 开头的请求 │ ├─────────────────────────────────────────────────────┤ │ Filter过滤器 │ │ 作用对匹配的请求做加工处理 │ │ 比如加请求头、改路径、限流、鉴权 │ └─────────────────────────────────────────────────────┘三者的协作流程客户端请求 ↓ Gateway 接收 ↓ ┌──────────────────────────────────────────────────┐ │ Predicate 断言匹配 │ │ 请求 /product/detail → 匹配 Path/product/** 路由 │ └──────────────┬───────────────────────────────────┘ ↓ ┌──────────────────────────────────────────────────┐ │ Pre Filter 前置过滤器链 │ │ 鉴权 → 加请求头 → 限流检查 │ └──────────────┬───────────────────────────────────┘ ↓ 转发到商品服务 ↓ ┌──────────────────────────────────────────────────┐ │ Post Filter 后置过滤器链 │ │ 改响应头 → 记录日志 → 统一响应格式 │ └──────────────┬───────────────────────────────────┘ ↓ 返回客户端四、路由配置方式4.1 yml 配置方式推荐spring:cloud:gateway:routes:# 商品服务路由-id:product-service-routeuri:lb://product-service# lb:// 表示负载均衡后跟服务名predicates:-Path/product/**# 匹配 /product/ 开头的路径filters:-StripPrefix1# 去掉第一层路径前缀# 订单服务路由-id:order-service-routeuri:lb://order-servicepredicates:-Path/order/**filters:-StripPrefix1StripPrefix1是一个常用过滤器请求/product/detail会被转成/detail发给商品服务。因为/product只是给 Gateway 用的路由前缀商品服务自己的 Controller 映射的是/detail。4.2 编程式配置RouteLocatorConfigurationpublicclassGatewayRouteConfig{BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilderbuilder){returnbuilder.routes().route(device-service-route,r-r.path(/device/**).filters(f-f.stripPrefix(1)).uri(lb://device-service)).build();}}实际开发中 90% 用 yml 配置编程式适合动态路由场景。五、断言Predicate路由匹配规则Gateway 内置了丰富的断言工厂可以组合使用断言含义示例Path匹配请求路径Path/product/**Method匹配 HTTP 方法MethodGET,POSTHeader匹配请求头HeaderX-Request-Id, \dHost匹配域名Host**.vending.comAfter指定时间之后After2026-01-01T00:00:0008:00Before指定时间之前Before2026-12-31T23:59:5908:00Between时间范围内Between2026-01-01T...,2026-06-01T...Query匹配请求参数QueryproductId, \dCookie匹配 CookieCookietoken, .RemoteAddr匹配 IPRemoteAddr192.168.1.0/24组合使用示例spring:cloud:gateway:routes:-id:product-service-routeuri:lb://product-servicepredicates:-Path/product/**-MethodGET,POST# 只允许 GET 和 POST-HeaderX-Api-Version,v2# 必须带 X-Api-Version: v2 头六、内置过滤器请求加工工具箱Gateway 内置了大量过滤器常用的有过滤器作用AddRequestHeader给请求加头AddRequestParameter给请求加参数StripPrefix去掉路径前缀常用RewritePath重写路径PrefixPath加路径前缀SetStatus设置响应状态码RequestRateLimiter请求限流配合 RedisAddResponseHeader给响应加头RewritePath 示例正则重写路径filters:-RewritePath/product/(?segment.*),/$\{segment}# /product/detail → /detail等价于 StripPrefix1七、自定义全局过滤器鉴权拦截内置过滤器是针对单条路由的如果你需要对所有请求做统一处理比如鉴权就用全局过滤器GlobalFilter。以 JWT 鉴权为例实现一个全局认证过滤器ComponentOrder(-100)// 优先级数字越小优先级越高。鉴权要最先执行publicclassAuthGlobalFilterimplementsGlobalFilter{// 不需要鉴权的白名单路径privatestaticfinalListStringWHITE_LISTList.of(/auth/login,/auth/register);OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){Stringpathexchange.getRequest().getURI().getPath();// 白名单直接放行if(WHITE_LIST.stream().anyMatch(path::startsWith)){returnchain.filter(exchange);}// 获取 TokenStringtokenexchange.getRequest().getHeaders().getFirst(Authorization);// Token 为空 → 401 未认证if(tokennull||token.isEmpty()){returnunauthorized(exchange,缺少认证信息);}// 校验 Tokentry{if(!token.startsWith(Bearer )){returnunauthorized(exchange,Token 格式错误);}Stringjwttoken.substring(7);// 这里调用 JWT 工具类校验简化示例StringuserIdJwtUtil.verify(jwt);if(userIdnull){returnunauthorized(exchange,Token 已失效);}// 校验通过把用户 ID 放到请求头传给下游服务ServerWebExchangemutatedExchangeexchange.mutate().request(r-r.header(X-User-Id,userId)).build();returnchain.filter(mutatedExchange);}catch(Exceptione){returnunauthorized(exchange,Token 校验失败);}}/** * 统一返回 401 未授权响应 */privateMonoVoidunauthorized(ServerWebExchangeexchange,Stringmessage){ServerHttpResponseresponseexchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().setContentType(MediaType.APPLICATION_JSON);Stringbody{\code\: 401, \message\: \message\};DataBufferbufferresponse.bufferFactory().wrap(body.getBytes());returnresponse.writeWith(Mono.just(buffer));}}这个过滤器的工作原理所有请求进来先经过它因为Order(-100)优先级高白名单路径直接放行其他路径检查Authorization头中的 JWT Token校验通过后把用户 ID 塞到请求头X-User-Id中传给下游服务校验失败返回 401 JSON 响应下游服务可以直接从请求头取用户 ID不用再重复解析 Token// 商品服务中GetMapping(/detail)publicStringgetProductDetail(RequestHeader(valueX-User-Id,requiredfalse)StringuserId){// userId 是网关传过来的已认证return用户 userId 查看商品详情;}八、网关整合 Nacos动态路由Gateway 整合 Nacos 后路由可以通过服务名自动发现不用写死 IPspring:cloud:gateway:discovery:locator:enabled:true# 开启服务发现自动路由lower-case-service-id:true# 服务名转小写开启后Gateway 会自动为每个注册到 Nacos 的服务创建路由http://网关地址/PRODUCT-SERVICE/product/detail ↓ 自动路由到 product-service 的某个实例 开启 lower-case-service-id 后 http://网关地址/product-service/product/detail但实际开发中一般不用自动路由而是用lb://服务名手动配置路由因为自动路由无法灵活配置 filters 和 predicates。动态路由指的是路由规则可以动态变更。可以通过 Nacos 配置中心管理路由 JSON监听变更后刷新路由表RefreshScopeConfigurationProperties(spring.cloud.gateway)publicclassDynamicRouteConfig{// 路由配置从 Nacos 读取变更时自动刷新}九、网关整合 Sentinel网关层限流在网关层做限流比在服务层做更高效——挡在门口总比放进来了再限要好。!-- Gateway 整合 Sentinel --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-sentinel-gateway/artifactId/dependencyspring:cloud:sentinel:transport:dashboard:127.0.0.1:8080filter:enabled:false# 关闭默认 Web 过滤器用 Gateway 专属适配ConfigurationpublicclassGatewayConfig{PostConstructpublicvoidinit(){// 自定义网关限流响应GatewayCallbackManager.setBlockHandler((exchange,ex)-{MapString,ObjectresultnewHashMap();result.put(code,429);result.put(message,请求太频繁请稍后再试);returnServerResponse.status(HttpStatus.TOO_MANY_REQUESTS).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(result));});}}在 Sentinel Dashboard 中可以看到 Gateway 专属的API 分组和网关路由维度的限流规则。十、网关最佳实践10.1 跨域配置spring:cloud:gateway:globalcors:cors-configurations:[/**]:allowed-origins:https://vending.example.comallowed-methods:*allowed-headers:*allow-credentials:truemax-age:3600在网关层统一配置 CORS 后下游服务就不用再单独配了。如果下游也配了 CORS 会导致Access-Control-Allow-Origin重复浏览器报错。10.2 超时配置spring:cloud:gateway:httpclient:connect-timeout:3000# 连接超时 3 秒response-timeout:10s# 响应超时 10 秒10.3 灰度发布通过自定义断言或过滤器根据请求头中的版本标记路由到不同版本的服务实例ComponentpublicclassGrayReleaseFilterimplementsGlobalFilter,Ordered{OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){Stringversionexchange.getRequest().getHeaders().getFirst(X-Gray-Version);if(v2.equals(version)){// 灰度流量路由到 v2 版本服务exchange.getAttributes().put(gray,true);}returnchain.filter(exchange);}OverridepublicintgetOrder(){return-90;}}配合 Nacos 给不同版本服务打标签Gateway 根据 Tag 路由到对应版本。十一、总结Gateway 是微服务的统一门卫掌握三个核心概念就够用了Route路由定义请求去哪、Predicate断言定义匹配条件、Filter过滤器定义加工逻辑。鉴权用自定义 GlobalFilter 统一处理限流交给 Sentinel 在网关层挡住跨域在网关统一配。守住这扇门后面的微服务群就安全了一大半。