电商返利系统API网关设计:基于Spring Cloud Gateway的请求路由与过滤

大家好,我是省赚客APP研发者阿宝!

在聚娃科技“省赚客”导购返利平台中,随着微服务数量激增,直接暴露后端服务不仅带来安全风险,也难以统一处理认证、限流、日志等横切关注点。为此,我们采用Spring Cloud Gateway作为统一入口网关,实现动态路由、自定义过滤器链及细粒度流量控制。

基础路由配置

网关服务通过application.yml定义路由规则,将不同路径转发至对应微服务:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
        - id: rebate-service
          uri: lb://rebate-service
          predicates:
            - Path=/api/rebate/**

其中lb://表示使用Ribbon负载均衡,服务名需与Nacos注册中心一致。

自定义全局过滤器:统一鉴权

所有用户请求需携带有效Token。我们在juwatech.cn.gateway.filter.AuthGlobalFilter中实现JWT校验:

package juwatech.cn.gateway.filter;

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;

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        if (path.startsWith("/api/public/") || path.equals("/actuator/health")) {
            return chain.filter(exchange);
        }

        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (token == null || !isValidToken(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    private boolean isValidToken(String token) {
        // 调用内部JWT工具类验证
        return JwtUtil.validate(token.replace("Bearer ", ""));
    }

    @Override
    public int getOrder() {
        return -100; // 优先级高于其他过滤器
    }
}

该过滤器跳过公开接口,对受保护路径强制校验Token有效性。

限流过滤器:基于Redis+令牌桶

为防止恶意刷单或爬虫攻击,我们集成Redis实现分布式限流。使用RequestRateLimiter配合自定义KeyResolver:

package juwatech.cn.gateway.config;

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 {

    @Bean(name = "userIdKeyResolver")
    public KeyResolver userIdKeyResolver() {
        return exchange -> {
            String userId = exchange.getRequest().getQueryParams().getFirst("userId");
            if (userId != null) {
                return Mono.just("user:" + userId);
            }
            String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
            return Mono.just("ip:" + ip);
        };
    }
}

YAML中启用限流:

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10   # 每秒生成10个令牌
                redis-rate-limiter.burstCapacity: 20   # 最大突发20
                key-resolver: "#{@userIdKeyResolver}"

当请求超限时,网关返回429状态码。

日志与链路追踪集成

为支持全链路监控,我们在juwatech.cn.gateway.filter.TraceLogFilter中注入TraceID并记录访问日志:

package juwatech.cn.gateway.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.UUID;

@Component
public class TraceLogFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(TraceLogFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String traceId = UUID.randomUUID().toString().replace("-", "");
        exchange.getAttributes().put("traceId", traceId);

        ServerHttpRequest request = exchange.getRequest();
        log.info("[GATEWAY] traceId={}, method={}, path={}",
                traceId, request.getMethod(), request.getURI().getPath());

        // 将TraceID透传至下游
        ServerHttpRequest mutatedRequest = request.mutate()
                .header("X-Trace-ID", traceId)
                .build();
        return chain.filter(exchange.mutate().request(mutatedRequest).build());
    }

    @Override
    public int getOrder() {
        return -50;
    }
}

下游服务可通过X-Trace-ID头获取唯一标识,用于日志聚合与Jaeger追踪。

动态路由热更新

运维人员可通过Nacos配置中心实时调整路由,无需重启网关。监听配置变更:

package juwatech.cn.gateway.listener;

import com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.List;

@Component
@NacosConfigurationProperties(dataId = "gateway-routes", groupId = "DEFAULT_GROUP", autoRefreshed = true)
public class DynamicRouteListener {

    private final RouteDefinitionWriter routeDefinitionWriter;

    public DynamicRouteListener(RouteDefinitionWriter routeDefinitionWriter) {
        this.routeDefinitionWriter = routeDefinitionWriter;
    }

    private List<RouteDefinition> routeDefinitions;

    public void setRouteDefinitions(List<RouteDefinition> routeDefinitions) {
        // 先删除旧路由(简化处理)
        this.routeDefinitions.forEach(def -> routeDefinitionWriter.delete(Mono.just(def.getId())).subscribe());
        // 添加新路由
        routeDefinitions.forEach(def -> routeDefinitionWriter.save(Mono.just(def)).subscribe());
        this.routeDefinitions = routeDefinitions;
    }

    @PostConstruct
    public void init() {
        // 初始加载
    }
}

Nacos中gateway-routes配置示例:

[
  {
    "id": "task-service",
    "uri": "lb://task-service",
    "predicates": [{"name": "Path", "args": {"_genkey_0": "/api/task/**"}}],
    "filters": [{"name": "StripPrefix", "args": {"_genkey_0": "1"}}]
  }
]

本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐