Spring Cloud Gateway 实战:路由配置、Redis 限流与 JWT 认证授权过滤器
1. 引言
Spring Cloud Gateway 是 Spring Cloud 生态中基于 Spring WebFlux 和 Project Reactor 构建的 API 网关。它提供了动态路由、限流、熔断、安全认证等核心功能,是现代微服务架构中不可或缺的组件。本文将围绕三个核心主题展开:路由配置、基于 Redis 的限流(RedisRateLimiter)以及JWT 认证与授权过滤器的实现,并提供完整的代码示例。
2. 环境与依赖准备
首先,创建一个 Spring Boot 项目,并添加以下核心依赖(以 Maven 为例):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</code>
确保你的 application.yml 中配置了 Redis 连接信息:
spring:
redis:
host: localhost
port: 6379
password:
database: 0
3. 路由配置
Spring Cloud Gateway 支持两种路由配置方式:基于配置文件的静态路由和基于代码的动态路由。
3.1 静态路由配置(YAML)
在 application.yml 中定义路由规则:
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- id: order-service-route
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
解释:
- id:路由唯一标识。
- uri:目标服务地址,
lb://表示通过服务发现(如 Nacos、Eureka)进行负载均衡。 - predicates:断言条件,
Path表示路径匹配。 - filters:网关过滤器链,
StripPrefix=1表示去掉路径前缀的第一段(例如/api/users/info转发到用户服务时变为/info)。
3.2 动态路由配置(Java Config)
通过 @Configuration 类定义路由:
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 GatewayRouteConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("product-service", r -> r
.path("/api/products/**")
.filters(f -> f.stripPrefix(1)
.addRequestHeader("X-Gateway-Source", "SpringCloudGateway"))
.uri("lb://PRODUCT-SERVICE"))
.route("auth-service", r -> r
.path("/auth/**")
.uri("lb://AUTH-SERVICE"))
.build();
}
}
4. 基于 Redis 的限流(RedisRateLimiter)
Spring Cloud Gateway 内置了 RequestRateLimiter 过滤器,可与 Redis 配合实现分布式限流。
4.1 配置限流过滤器
在路由配置中启用限流,并指定 key 解析器:
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒允许的请求数(令牌桶填充速率)
redis-rate-limiter.burstCapacity: 20 # 令牌桶容量(瞬时最大并发)
key-resolver: "#{@userKeyResolver}" # 限流键解析器 Bean 名称
4.2 实现 KeyResolver
限流键决定了如何区分用户或客户端。常见方式包括按用户 ID、IP 或 API 路径限流。
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class RateLimitConfig {
// 按请求 IP 限流
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
// 按用户 ID 限流(需从 JWT 或 Session 中提取)
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
// 假设从请求头中获取用户 ID
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (userId == null || userId.isEmpty()) {
userId = "anonymous";
}
return Mono.just(userId);
};
}
// 按 API 路径限流
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getPath().value()
);
}
}
然后在 application.yml 中引用对应的 Bean,例如 key-resolver: "#{@ipKeyResolver}"。
4.3 限流响应自定义
当请求被限流时,默认返回 HTTP 429 Too Many Requests。你可以通过自定义异常处理器来返回更友好的响应:
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
@Order(-1)
@Configuration
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// 处理限流异常
if (ex instanceof ResponseStatusException &amp;&amp;
((ResponseStatusException) ex).getStatus() == HttpStatus.TOO_MANY_REQUESTS) {
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = "{\"code\":429,\"message\":\"请求过于频繁,请稍后再试\"}";
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
// 其他异常处理...
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
return response.setComplete();
}
}
5. JWT 认证与授权过滤器
在微服务网关中,通常由网关统一进行 JWT 令牌的验证和用户信息的提取,然后将用户信息传递给下游服务。
5.1 JWT 工具类
首先创建一个 JWT 工具类,用于生成和解析令牌:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtil {
@Value("${jwt.secret:defaultSecretKey}")
private String secret;
@Value("${jwt.expiration:3600000}")
private Long expiration; // 毫秒
private Key key;
@PostConstruct
public void init() {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
}
// 生成令牌
public String generateToken(String username, Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
// 解析令牌
public Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
}
// 验证令牌
public boolean validateToken(String token) {
try {
parseToken(token);
return true;
} catch (Exception e) {
return false;
}
}
}
5.2 认证过滤器(GlobalFilter)
实现一个全局过滤器,对需要认证的路径进行 JWT 校验:
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.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {
private final JwtUtil jwtUtil;
// 白名单路径(无需认证)
private static final List<String> WHITE_LIST = List.of(
"/auth/login",
"/auth/register",
"/public/**"
);
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
// 检查是否为白名单路径
if (WHITE_LIST.stream().anyMatch(path::startsWith)) {
return chain.filter(exchange);
}
// 从请求头获取令牌
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
token = token.substring(7); // 去掉 "Bearer " 前缀
// 验证令牌
if (!jwtUtil.validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 解析用户信息并传递给下游服务
try {
Claims claims = jwtUtil.parseToken(token);
String username = claims.getSubject();
// 将用户信息添加到请求头中
exchange = exchange.mutate()
.request(builder -> builder.header("X-User-Name", username))
.build();
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; // 高优先级,最先执行
}
}
5.3 授权过滤器(自定义 GatewayFilter)
对于需要特定角色或权限的接口,可以创建自定义的 GatewayFilter:
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
@Component
public class RoleAuthorizationGatewayFilterFactory extends AbstractGatewayFilterFactory<RoleAuthorizationGatewayFilterFactory.Config> {
public RoleAuthorizationGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String requiredRole = config.getRequiredRole();
// 从请求头获取用户角色(实际应从 JWT 解析)
String userRole = exchange.getRequest().getHeaders().getFirst("X-User-Role");
if (userRole == null || !userRole.equals(requiredRole)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("requiredRole");
}
public static class Config {
private String requiredRole;
public String getRequiredRole() {
return requiredRole;
}
public void setRequiredRole(String requiredRole) {
this.requiredRole = requiredRole;
}
}
}
在路由配置中使用该过滤器:
spring:
cloud:
gateway:
routes:
- id: admin-route
uri: lb://ADMIN-SERVICE
predicates:
- Path=/api/admin/**
filters:
- StripPrefix=1
- name: RoleAuthorization
args:
requiredRole: ADMIN
6. 完整配置示例与测试
6.1 完整的 application.yml
server:
port: 8080
spring:
application:
name: api-gateway
redis:
host: localhost
port: 6379
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@ipKeyResolver}"
- id: auth-service
uri: lb://AUTH-SERVICE
predicates:
- Path=/auth/**
filters:
- StripPrefix=0
- id: admin-service
uri: lb://ADMIN-SERVICE
predicates:
- Path=/api/admin/**
filters:
- StripPrefix=1
- name: RoleAuthorization
args:
requiredRole: ADMIN
jwt:
secret: mySuperSecretKeyForJwtSigning123!
expiration: 3600000
6.2 测试步骤
- 启动 Redis 服务。
- 启动网关应用。
- 使用 Postman 或 curl 测试:
- 认证测试:访问
/api/users/1不带 Token,应返回 401。 - 限流测试:快速连续请求
/api/users/1(带有效 Token),超过 10 次/秒后应返回 429。 - 授权测试:访问
/api/admin/dashboard带普通用户 Token,应返回 403;带 ADMIN 角色 Token 应成功。
- 认证测试:访问
7. 总结
本文详细介绍了 Spring Cloud Gateway 的三个核心功能:
更多推荐



所有评论(0)