基于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开发者团队,转载请注明出处!

相关推荐
a努力。2 小时前
美团Java面试被问:Redis集群模式的工作原理
java·redis·后端·面试
一雨方知深秋2 小时前
面向对象编程
java·封装·this·构造器·static关键字·成员变量·javabean实体类
资生算法程序员_畅想家_剑魔2 小时前
Java常见技术分享-11-责任链模式
java·spring boot·责任链模式
计算机程序设计小李同学3 小时前
动漫之家系统设计与实现
java·spring boot·后端·web安全
心疼你的一切3 小时前
三菱FX5U PLC与C#通信开发指南
开发语言·单片机·c#
程序员阿鹏3 小时前
责任链模式
java·spring·servlet·tomcat·maven·责任链模式
Tim_103 小时前
【C++入门】04、C++浮点型
开发语言·c++
@淡 定3 小时前
Java内存模型(JMM)详解
java·开发语言
谈笑也风生3 小时前
经典算法题型之复数乘法(二)
开发语言·python·算法