基于Spring Cloud Gateway实现对外卖API请求的统一鉴权与流量染色

基于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-IdX-Traffic-Tag 透传至 order-service,且若存在灰度实例,则优先路由至该实例。

本文著作权归吃喝不愁app开发者团队,转载请注明出处!

相关推荐
zhougl99614 分钟前
Java 枚举类(enum)详解
java·开发语言·python
想七想八不如1140816 分钟前
2019机试真题
java·华为od·华为
yong999016 分钟前
基于势能原理的圆柱齿轮啮合刚度计算MATLAB程序实现
开发语言·matlab
恋爱绝缘体118 分钟前
Java语言提供了八种基本类型。六种数字类型【函数基数噶】
java·python·算法
lsx20240627 分钟前
R 数组:深入探索与高效使用
开发语言
星火开发设计27 分钟前
格式化输入输出:控制输出精度与对齐方式
开发语言·c++·学习·算法·函数·知识
MX_935939 分钟前
使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
java·后端·spring
弹简特40 分钟前
【JavaEE05-后端部分】使用idea社区版从零开始创建第一个 SpringBoot 程序
java·spring boot·后端
1104.北光c°42 分钟前
【黑马点评项目笔记 | 登录篇】Redis实现共享Session登录
java·开发语言·数据库·redis·笔记·spring·java-ee
爬山算法44 分钟前
Hibernate(81)如何在数据同步中使用Hibernate?
java·后端·hibernate