1. spring cloud gateway 是什么?
spring cloud gateway 是 spring cloud 生态系统中的现代化 api 网关组件,用于构建微服务架构中的统一入口网关。它基于 spring framework 5、project reactor 和 spring boot 2.x 构建,采用响应式编程模型,提供高性能、非阻塞式的 api 路由和横切关注点处理能力。
1.1 技术架构基础
- 响应式编程:基于 project reactor 的响应式流处理
- webflux:使用 spring webflux 而非传统的 servlet 模型
- 函数式编程:支持 java 8+ 的函数式编程风格
- 高性能:相比 zuul 1.x(阻塞式),性能提升显著
1.2 在微服务架构中的定位
客户端 → spring cloud gateway → 微服务a
→ 微服务b
→ 微服务c作为所有微服务的统一入口点,客户端只需要与网关交互,无需知道后端服务的具体位置。
2. spring cloud gateway 的作用
2.1 核心作用
- 统一入口:为所有微服务提供单一访问入口
- 路由转发:根据配置规则将请求路由到对应的服务
- 负载均衡:集成 spring cloud loadbalancer 实现服务发现和负载均衡
- 协议适配:支持 http/https、websocket 等协议
- 监控指标:收集请求日志、性能指标等监控数据
2.2 横切关注点处理
- 认证(authentication):统一处理用户身份验证
- 安全防护:防重放攻击、ip 黑白名单、基础安全头设置
- 限流熔断:实现请求限流、服务降级等保护机制
- 请求/响应修改:修改请求头、响应头等
3. spring cloud gateway 的特点
3.1 技术特点
- 响应式非阻塞:基于 reactor 的异步非阻塞处理模型
- 高性能:单机可处理数万 qps
- 灵活路由:支持多种路由匹配方式(path、host、method、header 等)
- 丰富过滤器:内置多种过滤器,支持自定义过滤器
- 动态配置:支持运行时动态修改路由配置
- 服务发现集成:无缝集成 eureka、consul、nacos 等注册中心
3.2 功能组件
- route(路由):定义请求如何被转发到目标服务
- predicate(断言):匹配 http 请求的各种条件
- filter(过滤器):修改请求和响应的逻辑
- globalfilter(全局过滤器):应用于所有路由的过滤器
4. 网关应该做什么 vs 不应该做什么
4.1 ✅ 网关应该做的事情
4.1.1 路由和转发
- 根据路径、主机名等条件路由请求
- 实现负载均衡和服务发现
- 处理协议转换(如 websocket)
4.1.2 基础安全(认证层面)
- 身份认证:验证 jwt token、api key 等的有效性
- token 验证:检查签名、过期时间、基本格式
- 安全头处理:添加 x-forwarded-for、x-real-ip 等头信息
- 基础防护:ip 黑白名单、防刷、防重放攻击
4.1.3 运维和监控
- 统一日志:记录访问日志、请求响应时间
- 指标收集:收集 qps、响应时间、错误率等指标
- 健康检查:提供网关自身的健康检查端点
- 限流熔断:基于 redis 或内存的请求限流
4.1.4 请求/响应处理
- 路径重写:stripprefix、prefixpath 等
- 头信息修改:添加、删除、修改请求/响应头
- 重试机制:对失败请求进行重试
4.2 ❌ 网关不应该做的事情
4.2.1 业务权限控制(授权)
- 不应该进行细粒度权限验证(如:用户a能否访问用户b的数据)
- 不应该验证业务级别的权限(如:是否有删除权限、编辑权限)
- 不应该替代业务服务的安全逻辑
4.2.2 业务逻辑处理
- 不应该包含业务逻辑(如:订单状态验证、库存检查)
- 不应该调用业务数据库进行复杂查询
- 不应该处理业务数据转换
4.2.3 复杂数据处理
- 不应该解析和验证复杂的请求体
- 不应该进行业务数据的校验和转换
- 不应该缓存业务数据
4.3 🏗️ 正确的安全分层架构
客户端 → 网关层(authentication) → 业务服务层(authorization)
↓ ↓
验证"你是谁" 验证"你能做什么"
token有效性验证 业务权限验证
基础安全防护 数据权限控制5. 详细使用示例
5.1 基础环境搭建
5.1.1 maven 依赖配置
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/pom/4.0.0"
xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xsi:schemalocation="http://maven.apache.org/pom/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<parent>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-parent</artifactid>
<version>2.7.0</version>
<relativepath/>
</parent>
<groupid>com.example</groupid>
<artifactid>spring-cloud-gateway-demo</artifactid>
<version>1.0.0</version>
<name>spring-cloud-gateway-demo</name>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<!-- spring cloud gateway 核心依赖 -->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-gateway</artifactid>
</dependency>
<!-- 服务发现客户端(用于集成注册中心) -->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-loadbalancer</artifactid>
</dependency>
<!-- actuator 监控端点 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-actuator</artifactid>
</dependency>
<!-- redis 依赖(用于限流) -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-redis-reactive</artifactid>
</dependency>
</dependencies>
<dependencymanagement>
<dependencies>
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-dependencies</artifactid>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencymanagement>
</project>5.1.2 基础配置文件 (application.yml)
server:
port: 9000 # 网关服务端口
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowedorigins: "*" # 允许所有源
allowedmethods: "*" # 允许所有http方法
allowedheaders: "*" # 允许所有请求头
allowcredentials: true # 允许携带凭证
# 路由配置
routes:
# 用户服务路由 - 网关只负责路由和基础认证
- id: user-service-route
uri: http://localhost:8081
predicates:
- path=/api/users/** # 匹配用户相关路径
filters:
- stripprefix=2 # 去掉 /api/users 前缀,实际转发到 /**
# 订单服务路由
- id: order-service-route
uri: http://localhost:8082
predicates:
- path=/api/orders/**
filters:
- stripprefix=2
# actuator 监控配置
management:
endpoints:
web:
exposure:
include: '*' # 暴露所有监控端点
endpoint:
gateway:
enabled: true # 启用网关管理端点5.2 路由配置详解
5.2.1 java 代码配置路由
package com.example.gateway.config;
import org.springframework.cloud.gateway.route.routelocator;
import org.springframework.cloud.gateway.route.builder.routelocatorbuilder;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
/**
* 网关路由配置类
* 使用 java 代码方式配置路由,相比配置文件更加灵活
* 注意:这里只配置路由规则,不包含业务权限逻辑
*/
@configuration
public class gatewayrouteconfig {
/**
* 配置自定义路由规则
* @param builder routelocatorbuilder 用于构建路由
* @return routelocator 路由定位器
*/
@bean
public routelocator customroutelocator(routelocatorbuilder builder) {
return builder.routes()
// 用户服务路由
.route("user-service", r -> r
.path("/api/users/**") // 匹配路径
.uri("http://localhost:8081") // 目标服务地址
.filters(f -> f.stripprefix(2)) // 去掉前两个路径段 (/api/users)
)
// 订单服务路由
.route("order-service", r -> r
.path("/api/orders/**")
.uri("http://localhost:8082")
.filters(f -> f.stripprefix(2))
)
// 公开api路由(无需认证)
.route("public-api", r -> r
.path("/api/public/**")
.uri("http://localhost:8083")
.filters(f -> f.stripprefix(2))
)
.build();
}
}5.3 断言(predicates)使用示例
package com.example.gateway.config;
import org.springframework.cloud.gateway.route.routelocator;
import org.springframework.cloud.gateway.route.builder.routelocatorbuilder;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
/**
* 各种断言使用示例
* 断言用于定义路由匹配条件,只有满足条件的请求才会被路由
* 这些都是基础的路由条件,不涉及业务逻辑
*/
@configuration
public class predicateconfig {
@bean
public routelocator predicateroutelocator(routelocatorbuilder builder) {
return builder.routes()
// 1. path 断言:基于url路径匹配
.route("path-route", r -> r
.path("/api/v1/**", "/api/v2/**") // 支持多个路径模式
.uri("http://localhost:8081")
)
// 2. method 断言:基于http方法匹配
.route("method-route", r -> r
.path("/api/method/**")
.and()
.method("get", "post") // 只匹配get和post请求
.uri("http://localhost:8082")
)
// 3. header 断言:基于请求头匹配
.route("header-route", r -> r
.path("/api/header/**")
.and()
.header("x-api-version", "v1") // 必须包含指定请求头
.uri("http://localhost:8083")
)
// 4. query 断言:基于查询参数匹配
.route("query-route", r -> r
.path("/api/query/**")
.and()
.query("version", "1.0") // 查询参数 version=1.0
.uri("http://localhost:8084")
)
// 5. host 断言:基于host头匹配
.route("host-route", r -> r
.host("api.example.com", "api.test.com") // 匹配指定域名
.uri("http://localhost:8085")
)
.build();
}
}5.4 过滤器(filters)使用示例
5.4.1 内置过滤器配置
package com.example.gateway.config;
import org.springframework.cloud.gateway.filter.ratelimit.redisratelimiter;
import org.springframework.cloud.gateway.route.routelocator;
import org.springframework.cloud.gateway.route.builder.routelocatorbuilder;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
/**
* 内置过滤器使用示例
* 过滤器用于修改请求和响应,但只做基础处理,不涉及业务逻辑
*/
@configuration
public class filterconfig {
@bean
public routelocator filterroutelocator(routelocatorbuilder builder) {
return builder.routes()
// 1. 添加请求头过滤器
.route("add-header-route", r -> r
.path("/api/add-header/**")
.filters(f -> f
// 添加网关来源标识
.addrequestheader("x-gateway-source", "spring-cloud-gateway")
// 添加请求时间戳
.addrequestheader("x-request-timestamp",
string.valueof(system.currenttimemillis()))
)
.uri("http://localhost:8081")
)
// 2. 添加响应头过滤器
.route("add-response-header-route", r -> r
.path("/api/add-response-header/**")
.filters(f -> f
.addresponseheader("x-gateway-processed", "true")
)
.uri("http://localhost:8082")
)
// 3. 路径重写过滤器
.route("strip-prefix-route", r -> r
.path("/api/strip/**")
.filters(f -> f.stripprefix(1)) // 去掉第一个路径段
.uri("http://localhost:8083")
)
// 4. 限流过滤器(使用redis)
.route("rate-limiter-route", r -> r
.path("/api/rate-limiter/**")
.filters(f -> f
.requestratelimiter()
.ratelimiter(redisratelimiter.class)
// 配置限流参数:每秒5个请求,突发容量10个
.configure(c -> c.setburstcapacity(10).setreplenishrate(5))
)
.uri("http://localhost:8084")
)
// 5. 重试过滤器
.route("retry-route", r -> r
.path("/api/retry/**")
.filters(f -> f.retry(3)) // 失败时重试3次
.uri("http://localhost:8085")
)
.build();
}
}5.5 网关安全配置(正确的做法)
5.5.1 网关层认证过滤器(只做身份认证)
package com.example.gateway.filter;
import org.springframework.cloud.gateway.filter.gatewayfilterchain;
import org.springframework.cloud.gateway.filter.globalfilter;
import org.springframework.core.ordered;
import org.springframework.http.httpstatus;
import org.springframework.http.server.reactive.serverhttprequest;
import org.springframework.stereotype.component;
import org.springframework.web.server.serverwebexchange;
import reactor.core.publisher.mono;
import java.util.arrays;
import java.util.list;
/**
* 网关认证过滤器 - 只负责身份认证,不负责权限授权
*
* 职责范围:
* ✅ 验证jwt token的有效性(签名、过期时间)
* ✅ 验证token的基本格式
* ✅ 将用户信息传递给下游服务
* ❌ 不进行业务权限验证
* ❌ 不调用业务数据库
* ❌ 不处理复杂的业务逻辑
*/
@component
public class authenticationglobalfilter implements globalfilter, ordered {
// 公开路径列表(无需认证的路径)
private static final list<string> public_paths = arrays.aslist(
"/api/public/**",
"/api/auth/login",
"/api/auth/register",
"/actuator/**"
);
/**
* 全局过滤器执行逻辑
* @param exchange 当前请求交换对象
* @param chain 过滤器链
* @return mono<void> 响应式返回
*/
@override
public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
string requestpath = exchange.getrequest().geturi().getpath();
// 1. 检查是否为公开路径,如果是则直接放行
if (ispublicpath(requestpath)) {
return chain.filter(exchange);
}
// 2. 从请求头中提取认证令牌
string token = extracttoken(exchange);
if (token == null || token.isempty()) {
return handleunauthorized(exchange, "missing authentication token");
}
try {
// 3. 验证令牌有效性(只验证签名和过期时间,不验证业务权限)
if (!validatetokensignatureandexpiry(token)) {
return handleunauthorized(exchange, "invalid or expired token");
}
// 4. 从令牌中提取用户基本信息
string userid = extractuseridfromtoken(token);
string userroles = extractuserrolesfromtoken(token);
// 5. 将用户信息添加到请求头,传递给下游业务服务
// 下游服务将基于这些信息进行具体的权限验证
serverhttprequest modifiedrequest = exchange.getrequest().mutate()
.header("x-user-id", userid)
.header("x-user-roles", userroles)
.header("x-authenticated", "true")
.build();
return chain.filter(exchange.mutate().request(modifiedrequest).build());
} catch (exception e) {
return handleunauthorized(exchange, "authentication processing failed: " + e.getmessage());
}
}
/**
* 判断路径是否为公开路径
* @param path 请求路径
* @return 是否为公开路径
*/
private boolean ispublicpath(string path) {
return public_paths.stream()
.anymatch(publicpath -> path.matches(publicpath.replace("**", ".*")));
}
/**
* 从请求中提取认证令牌
* 支持 bearer token 和自定义 header
*/
private string extracttoken(serverwebexchange exchange) {
// 优先从 authorization 头获取
string authheader = exchange.getrequest().getheaders().getfirst("authorization");
if (authheader != null && authheader.startswith("bearer ")) {
return authheader.substring(7); // 去掉 "bearer " 前缀
}
// 也可以从自定义头或查询参数获取
string tokenheader = exchange.getrequest().getheaders().getfirst("x-api-token");
if (tokenheader != null) {
return tokenheader;
}
return null;
}
/**
* 验证令牌签名和过期时间
* 注意:这里只做基础验证,不做业务权限验证
*/
private boolean validatetokensignatureandexpiry(string token) {
// 实际项目中应该使用 jwt 库进行验证
// 这里简化处理,实际应该验证签名、过期时间等
try {
// 伪代码:验证 jwt 签名和过期时间
// jwts.parser().setsigningkey(secret).parseclaimsjws(token);
return token.length() > 20; // 简单验证
} catch (exception e) {
return false;
}
}
/**
* 从令牌中提取用户id
* 实际项目中应该解析 jwt payload
*/
private string extractuseridfromtoken(string token) {
// 伪代码:从 jwt 中提取用户id
return "user123"; // 简化处理
}
/**
* 从令牌中提取用户角色
* 实际项目中应该解析 jwt payload 中的角色信息
*/
private string extractuserrolesfromtoken(string token) {
// 伪代码:从 jwt 中提取角色
return "user,premium"; // 简化处理
}
/**
* 处理未授权请求
*/
private mono<void> handleunauthorized(serverwebexchange exchange, string message) {
exchange.getresponse().setstatuscode(httpstatus.unauthorized);
exchange.getresponse().getheaders().add("content-type", "application/json");
string response = "{\"error\":\"unauthorized\",\"message\":\"" + message + "\"}";
return exchange.getresponse().writewith(
mono.just(exchange.getresponse().bufferfactory().wrap(response.getbytes()))
);
}
/**
* 设置过滤器执行顺序
* 数值越小,优先级越高
*/
@override
public int getorder() {
return -100; // 在很早的阶段执行认证
}
}5.5.2 业务服务层权限控制(真正的授权)
// 用户服务中的权限控制示例
package com.example.userservice.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.security.access.prepost.preauthorize;
import org.springframework.web.server.responsestatusexception;
import org.springframework.http.httpstatus;
/**
* 用户控制器 - 在业务服务中进行真正的权限验证
*
* 网关已经完成了身份认证,这里进行业务级别的权限授权
*/
@restcontroller
@requestmapping("/api")
public class usercontroller {
/**
* 获取用户详情
* 需要验证当前用户是否有权限访问目标用户
*/
@getmapping("/users/{userid}")
public user getuser(@requestheader("x-user-id") string currentuserid,
@requestheader("x-user-roles") string userroles,
@pathvariable string userid) {
// 1. 验证是否是访问自己的信息(普通用户只能访问自己)
if (!currentuserid.equals(userid)) {
// 2. 如果不是访问自己,检查是否具有管理员角色
if (!userroles.contains("admin")) {
throw new responsestatusexception(httpstatus.forbidden,
"insufficient permissions to access this user data");
}
}
// 3. 执行业务逻辑
return userservice.findbyid(userid);
}
/**
* 删除用户 - 需要管理员权限
* 使用 spring security 注解进行权限控制
*/
@preauthorize("hasrole('admin')")
@deletemapping("/users/{userid}")
public void deleteuser(@pathvariable string userid) {
userservice.delete(userid);
}
}5.6 日志和监控配置
5.6.1 访问日志过滤器
package com.example.gateway.filter;
import org.springframework.cloud.gateway.filter.gatewayfilterchain;
import org.springframework.cloud.gateway.filter.globalfilter;
import org.springframework.core.ordered;
import org.springframework.stereotype.component;
import org.springframework.web.server.serverwebexchange;
import reactor.core.publisher.mono;
import java.time.localdatetime;
/**
* 访问日志全局过滤器
* 记录请求的基本信息,用于监控和审计
* 注意:只记录基础信息,不记录敏感业务数据
*/
@component
public class accesslogglobalfilter implements globalfilter, ordered {
@override
public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
// 记录请求开始时间
long starttime = system.currenttimemillis();
string path = exchange.getrequest().geturi().getpath();
string method = exchange.getrequest().getmethodvalue();
string clientip = getclientip(exchange);
system.out.println(string.format(
"[access-log] %s | %s | %s | %s",
localdatetime.now(), method, path, clientip
));
// 继续处理请求,并记录响应时间
return chain.filter(exchange).doonterminate(() -> {
long duration = system.currenttimemillis() - starttime;
system.out.println(string.format(
"[access-log] response time: %d ms for %s", duration, path
));
});
}
private string getclientip(serverwebexchange exchange) {
string xforwardedfor = exchange.getrequest().getheaders().getfirst("x-forwarded-for");
if (xforwardedfor != null && !xforwardedfor.isempty()) {
return xforwardedfor.split(",")[0].trim();
}
return exchange.getrequest().getremoteaddress() != null ?
exchange.getrequest().getremoteaddress().getaddress().gethostaddress() : "unknown";
}
@override
public int getorder() {
return ordered.lowest_precedence - 1; // 在最后执行,确保能获取到完整信息
}
}5.7 完整的启动类
package com.example.gateway;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
/**
* spring cloud gateway 网关启动类
*
* 设计原则:
* ✅ 网关只负责:路由转发、身份认证、基础安全、监控日志
* ❌ 网关不负责:业务权限、业务逻辑、数据验证
*
* 启动后可用端点:
* - 网关服务:http://localhost:9000
* - 路由信息:http://localhost:9000/actuator/gateway/routes
* - 健康检查:http://localhost:9000/actuator/health
*/
@springbootapplication
public class gatewayapplication {
public static void main(string[] args) {
springapplication.run(gatewayapplication.class, args);
system.out.println("=====================================");
system.out.println("spring cloud gateway 启动成功!");
system.out.println("网关地址: http://localhost:9000");
system.out.println("路由管理: http://localhost:9000/actuator/gateway/routes");
system.out.println("=====================================");
}
}6. 最佳实践总结
6.1 网关职责边界
- 网关层:路由 + 认证 + 基础安全 + 监控
- 业务层:授权 + 业务逻辑 + 数据验证
6.2 性能优化建议
- 合理配置线程池和连接池
- 避免在过滤器中执行耗时操作
- 使用缓存减少重复计算
6.3 安全最佳实践
- 网关只做身份认证,不做权限授权
- 敏感信息不要在网关层处理
- 使用 https 保护传输安全
6.4 监控和运维
- 集成 prometheus + grafana
- 配置合理的健康检查
- 记录详细的访问日志
这份文档详细说明了 spring cloud gateway 的正确使用方式,特别强调了网关的职责边界,避免了常见的架构设计误区。所有示例都包含详细的中文注释,帮助您理解每个组件的正确用途和实现方式。
到此这篇关于spring cloud gateway详细使用最佳实践的文章就介绍到这了,更多相关spring cloud gateway使用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论