基于Spring Cloud Gateway实现对外卖API请求的统一鉴权与流量染色
在外卖平台微服务架构中,所有客户端(App、小程序、第三方合作方)的请求均通过 Spring Cloud Gateway 统一入口进入后端系统。为保障安全性和支持灰度发布、A/B测试等场景,需在网关层实现统一鉴权 与流量染色 。本文基于 baodanbao.com.cn.* 包结构,提供完整的自定义 GlobalFilter 实现方案。
项目依赖与基础配置
首先引入必要依赖:
xml
<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</artifactId>
</dependency>
在 application.yml 中配置路由:
yaml
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
- id: promo-service
uri: lb://promo-service
predicates:
- Path=/api/promo/**

实现统一鉴权 GlobalFilter
创建 AuthGlobalFilter,校验请求头中的 X-Access-Token 是否有效,并解析用户信息:
java
package baodanbao.com.cn.gateway.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
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, Ordered {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("X-Access-Token");
if (token == null || token.isEmpty()) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 查询Redis验证token有效性
String userId = redisTemplate.opsForValue().get("auth:token:" + token);
if (userId == null) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 将用户ID注入请求头,供下游服务使用
ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
@Override
public int getOrder() {
return -100; // 优先级高于其他过滤器
}
}
实现流量染色 GlobalFilter
流量染色指根据特定规则(如Header、Cookie、参数)为请求打上标签(如 env=gray),用于路由到灰度实例。以下实现基于 X-Traffic-Tag 头部:
java
package baodanbao.com.cn.gateway.filter;
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;
@Component
public class TrafficTaggingFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String tag = exchange.getRequest().getHeaders().getFirst("X-Traffic-Tag");
if (tag != null && !tag.trim().isEmpty()) {
// 将流量标签透传至下游
ServerHttpRequest mutated = exchange.getRequest().mutate()
.header("X-Traffic-Tag", tag.trim())
.build();
return chain.filter(exchange.mutate().request(mutated).build());
}
// 无标签则默认走主干
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -90; // 在鉴权之后执行
}
}
结合Nacos实现动态灰度路由
若使用 Nacos 作为注册中心,可在下游服务实例元数据中标记 traffic.tag=gray,并在网关中扩展 LoadBalancer 实现标签匹配。但更轻量的方式是通过 Gateway Predicate + Header Route 实现:
yaml
spring:
cloud:
gateway:
routes:
- id: order-service-gray
uri: lb://order-service
predicates:
- Path=/api/order/**
- Header=X-Traffic-Tag, gray
metadata:
nacos.weight: 1
- id: order-service-prod
uri: lb://order-service
predicates:
- Path=/api/order/**
注意:上述配置要求 order-service 的灰度实例在注册时携带 metadata.traffic.tag=gray,并配合自定义 RibbonRule 或 Spring Cloud LoadBalancer 的 ServiceInstanceListSupplier 实现标签过滤。
日志追踪与上下文传递
为便于排查问题,可将染色标签和用户ID写入MDC:
java
// 在TrafficTaggingFilter或AuthGlobalFilter中补充
import org.slf4j.MDC;
String userId = ...;
String tag = ...;
MDC.put("user_id", userId);
MDC.put("traffic_tag", tag);
配合 Logback 配置:
xml
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level [%X{user_id},%X{traffic_tag}] %logger{36} - %msg%n</pattern>
测试验证
使用 curl 模拟带染色标签的请求:
bash
curl -H "X-Access-Token: valid_token_123" \
-H "X-Traffic-Tag: gray" \
http://gateway:8080/api/order/123
网关应将 X-User-Id 和 X-Traffic-Tag 透传至 order-service,且若存在灰度实例,则优先路由至该实例。
本文著作权归吃喝不愁app开发者团队,转载请注明出处!