深入剖析Spring Cloud Gateway,自定义过滤器+断言组合成拦截器链实现Token认证

发布于:2025-06-18 ⋅ 阅读:(11) ⋅ 点赞:(0)

一、Spring Cloud Gateway网关的整体架构

Spring Cloud Gateway 是 Spring Cloud 官方推出的网关解决方案,旨在替代 Netflix Zuul 1.x。其底层基于 Spring WebFlux + Reactor 模型 构建,具备响应式、异步非阻塞的高性能特点。

1. 整体架构图

                   +-------------+
     Client  --->  |  Netty HTTP |  --->  Filter Chain --->  Route Matching
                   +-------------+                            ↓
                                                             URI Rewrite
                                                             ↓
                                                     Load Balancer / WebClient
                                                             ↓
                                                          Target Service

2. 核心组件剖析

1. Netty Server(底层通信)

  • 基于 Spring WebFlux + Netty 实现异步非阻塞的请求接入。

  • Reactor Netty 作为服务器(取代 Tomcat),使用 NIO 支持高并发处理。

  • 请求通过 HttpServer 接入,并封装为 ServerWebExchange 对象。


2. Route(路由定义)

核心配置单位,决定请求的转发目标。

路由配置组成:
spring:
  cloud:
    gateway:
      routes:
        - id: my-route
          uri: http://localhost:8081
          predicates:
            - Path=/api/**
          filters:
            - AddRequestHeader=token, my-token
路由核心类:
  • Route: 单个路由对象,包含 id、uri、predicate、filter 等信息。

  • RouteDefinition: YAML 或 Java DSL 中定义的原始配置。

  • RouteLocator: 路由查找器,默认实现是 CachingRouteLocator,缓存所有 Route


3. Predicate(断言)

  • 用于匹配请求是否符合某个路由。

  • 每个断言是一个实现了 GatewayPredicate 的类,底层是 Predicate<ServerWebExchange>

  • 常见内置断言:Path, Method, Header, Host, Query, RemoteAddr 等。

示例:
exchange -> exchange.getRequest().getURI().getPath().startsWith("/api/")

4. Filter(过滤器)

过滤器链结构:
  • Spring Cloud Gateway 采用 责任链模式 管理所有过滤器。

  • 分为两类:

    • 全局过滤器(GlobalFilter):作用于所有请求,如 NettyRoutingFilter, LoadBalancerClientFilter

    • 局部过滤器(GatewayFilter):作用于具体的 Route。

核心类:
  • GatewayFilterChain: 核心接口,调用 filter(ServerWebExchange, GatewayFilterChain) 方法。

  • OrderedGatewayFilter: 带排序的过滤器包装类。

  • FilterDefinition: YAML 中的过滤器配置项。

过滤器执行流程图:
Client --> GlobalFilter1 --> GatewayFilterA --> GatewayFilterB --> GlobalFilter2 --> Backend
内置过滤器示例:
  • AddRequestHeaderGatewayFilterFactory

  • RewritePathGatewayFilterFactory

  • HystrixGatewayFilterFactory(Spring Cloud CircuitBreaker)


5. ServerWebExchange(上下文封装)

  • 封装请求 (ServerHttpRequest) 和响应 (ServerHttpResponse)。

  • 贯穿整个生命周期,类似于 Servlet 中的 HttpServletRequest/Response。


6. 转发请求(NettyRoutingFilter 或 LoadBalancerClientFilter)

  • NettyRoutingFilter: 默认将请求转发给目标 URI。

  • LoadBalancerClientFilter: 在 URI 为 lb:// 时调用注册中心负载均衡(集成 Ribbon、Spring Cloud LoadBalancer)。

示例:
// 基于 WebClient 发起转发请求(WebFlux 非阻塞客户端)
webClient.method(request.getMethod())
         .uri(targetUri)
         .headers(httpHeaders -> ...)
         .exchange()

3. 执行流程详解

请求处理核心流程:

Step 1: Netty 接收请求并生成 ServerWebExchange
Step 2: RouteLocator 匹配路由
Step 3: 断言 Predicate 判断是否命中路由
Step 4: 构造过滤器链(全局 + 路由)
Step 5: 执行 GatewayFilterChain
Step 6: 最终由 NettyRoutingFilter 或 LoadBalancerClientFilter 发起转发
Step 7: 响应回传至客户端

4. 响应式编程模型(核心基础)

  • Spring Cloud Gateway 是基于 Reactor 模型构建,响应式、非阻塞、背压友好。

  • Filter 链通过 Mono.defer(() -> filter(...)).then(...) 串联。

  • 所有逻辑必须是非阻塞,否则会违背设计初衷,导致性能问题。


5. 扩展点

扩展点 接口或类 说明
自定义断言 RoutePredicateFactory 继承 AbstractRoutePredicateFactory
自定义过滤器 GatewayFilterFactory 继承 AbstractGatewayFilterFactory
自定义全局过滤器 GlobalFilter 直接实现即可
动态路由注册 实现 RouteDefinitionLocator 接口 支持 Nacos、数据库等动态配置
自定义负载均衡策略 ReactiveLoadBalancer Spring Cloud LoadBalancer 接口

6. 与 Spring 生态集成

  • 注册中心集成:通过 Spring Cloud DiscoveryClient + LoadBalancerClientFilter 实现服务名解析。

  • 熔断限流:结合 Sentinel、Resilience4j、RateLimiter(内置 TokenBucket 限流器)实现。

  • 链路追踪:集成 Sleuth,自动注入 TraceId 进入 header。

  • 认证鉴权:通过局部或全局过滤器在 Filter 中实现 JWT 校验、权限认证。


7. 性能优势

特性 Spring Cloud Gateway 优势
异步非阻塞 基于 Reactor 模型,比 Zuul 1.x 阻塞式性能更优
支持服务发现 与 Spring Cloud 原生集成
灵活可扩展 Filter 和 Predicate 都易于扩展
强大配置能力 基于 YAML/Java DSL 路由配置能力

8. 底层与 Zuul 对比

对比项 Spring Cloud Gateway Netflix Zuul 1.x
编程模型 响应式(WebFlux) 阻塞(Servlet)
性能 高并发、低延迟 适中
路由配置方式 灵活,支持 Predicate+Filter 相对单一
可扩展性 非常强 一般

二、深入剖析断言(Predicate)

Spring Cloud Gateway 中的 断言(Predicate) 是路由匹配的核心机制之一,作用是判断当前请求是否满足条件,是否应该命中该路由。断言具备强大的表达能力,底层通过组合式 Predicate 实现。


1. 断言(Predicate)概念简述

  • 本质是 Predicate<ServerWebExchange>,判断当前请求上下文是否满足某些规则。

  • 每个路由可以配置多个断言,必须全部通过,才会命中路由(逻辑与关系)

  • 断言通过配置文件或 Java DSL 配置,最终转为 RoutePredicateFactory 执行逻辑。


2. 核心断言分类(内置 PredicateFactory)

Spring Cloud Gateway 提供了丰富的内置断言工厂(可扩展)。下面按功能分类剖析它们的参数和底层实现:


1. Path 断言

功能:基于请求路径进行匹配。

配置示例:

predicates:
  - Path=/api/v1/**,/admin/**

参数解析:

  • 接收一个或多个 Ant 风格路径。

  • 支持通配符 ***

  • 会自动与 Request.Path 比较。

底层类:

  • PathRoutePredicateFactory

  • 实现 apply(Config config) 返回 exchange -> pathMatcher.match(pathPattern, requestPath)


2. Method 断言

功能:匹配 HTTP 方法。

配置示例:

predicates:
  - Method=GET,POST

底层类MethodRoutePredicateFactory

参数说明

  • 枚举形式传入,如 GET、POST、PUT、DELETE 等。

  • 会转换为 HttpMethod 比较 exchange.getRequest().getMethod()


3. Header 断言

功能:匹配请求头信息。

配置示例:

predicates:
  - Header=X-Request-Id, ^[0-9]+$

参数说明

  • 第一个参数为请求头 key,第二个为正则表达式。

  • 正则匹配请求头的值。

底层类HeaderRoutePredicateFactory


4. Query 断言

功能:匹配请求 query 参数。

配置示例:

predicates:
  - Query=version, ^v1$

底层类QueryRoutePredicateFactory

说明

  • key 是 query 参数名。

  • value 是正则表达式,用于匹配参数值。


5. Host 断言

功能:匹配请求中的 Host 头部。

配置示例:

predicates:
  - Host=**.example.org

底层类HostRoutePredicateFactory

说明

  • 支持通配符:*.example.org

  • 实际读取 Host 头部进行匹配。


6. RemoteAddr 断言

功能:基于 IP 地址匹配。

配置示例:

predicates:
  - RemoteAddr=192.168.1.0/24

底层类RemoteAddrRoutePredicateFactory

说明

  • 支持 CIDR 表达式(子网匹配)。

  • 注意:必须配置 X-Forwarded-For 支持或确保 Netty 获取真实 IP。


7. After / Before / Between 断言(基于时间)

predicates:
  - After=2025-01-01T00:00:00+08:00
  - Before=2025-12-31T23:59:59+08:00

底层类

  • AfterRoutePredicateFactory

  • BeforeRoutePredicateFactory

  • BetweenRoutePredicateFactory

说明

  • ISO-8601 时间格式,必须包含时区。

  • 匹配当前请求时间是否在设定时间点之前、之后或区间内。


8. Cookie 断言

predicates:
  - Cookie=session, ^[a-z0-9]+$

底层类CookieRoutePredicateFactory

说明

  • 断言某个 Cookie 存在并满足正则匹配。


3. Predicate 参数结构

YAML 配置风格

- Name=arg1,arg2,arg3

内部通过 Spring Boot 自动绑定为 RoutePredicateFactory.Config 子类。

例如:

public class HeaderRoutePredicateFactory
       extends AbstractRoutePredicateFactory<HeaderConfig> {

   public static class HeaderConfig {
       private String header;
       private String regexp;
   }

   public Predicate<ServerWebExchange> apply(HeaderConfig config) {
       return exchange -> {
           List<String> values = exchange.getRequest().getHeaders().get(config.getHeader());
           return values != null && values.stream().anyMatch(v -> v.matches(config.getRegexp()));
       };
   }
}

4. 自定义 Predicate 实现

1. 实现类继承方式

继承:

public class MyCustomRoutePredicateFactory
    extends AbstractRoutePredicateFactory<MyConfig> {
    
    public Predicate<ServerWebExchange> apply(MyConfig config) {
        return exchange -> {
            // 自定义判断逻辑
            return true;
        };
    }
}

2. 配置方式

predicates:
  - MyCustom=configValue1,configValue2

3. 自动注册

只要类名以 RoutePredicateFactory 结尾,Spring 会自动注册。


5. 组合断言(多断言逻辑与)

predicates:
  - Path=/api/**
  - Method=GET
  - Header=X-Auth-Token, .+

执行顺序:按配置顺序逐一判断,全部为 true 才匹配成功


6. 调试技巧与陷阱

问题类型 原因或建议
路由不匹配 断言顺序中某一个未通过
时间断言失败 时区不一致或格式错误
IP 断言无效 网关前有代理,建议配置 ForwardedHeaderFilter
多参数不生效 多个 query/header 应用多个断言条目,而不是一个断言多个值
正则不匹配 注意正则表达式要转义,避免 YAML 被错误解析

三、深入剖析过滤器(Filter)

1. 执行总流程概览

Spring Cloud Gateway 请求生命周期:

1. 客户端发起请求
2. Netty 接收请求并封装为 ServerWebExchange
3. RouteLocator 查找所有 Route(含断言 Predicate)
4. Predicate 执行(逐个判断,全部通过才命中 Route)
5. 构建 GatewayFilterChain(全局过滤器 + 路由过滤器)
6. 依序执行过滤器链(前置逻辑 → 发起请求转发 → 后置逻辑)
7. 收到目标服务响应,经过过滤器返回响应

2. 断言执行原理与顺序

断言是路由匹配的前置条件,执行在过滤器构造 之前

✅ 原理分析

  1. 所有 RouteRouteLocator 管理(比如 CachingRouteLocator)。

  2. 每个 RouteDefinition 包含一组 PredicateDefinition

  3. 加载时这些断言通过对应的 RoutePredicateFactory 转换为 Predicate<ServerWebExchange>

  4. 网关请求处理器 RoutePredicateHandlerMapping 会将所有路由依次尝试匹配这些 Predicate。

// RoutePredicateHandlerMapping 中的匹配逻辑(简化)
for (Route route : this.routeLocator.getRoutes()) {
    if (route.getPredicate().test(exchange)) {
        return route;
    }
}

✅ 顺序与逻辑

  • 按路由在配置文件中定义的顺序查找。

  • 每个路由内部的多个断言为 AND 逻辑,所有断言返回 true 才命中。

  • 匹配成功后立即终止查找,执行对应的过滤器链。


3. 过滤器执行原理与顺序

过滤器构成了 真正的处理链,所有请求响应的处理均由其决定。

✅ 执行模型

  • 过滤器链使用 责任链模式 构建,封装为 GatewayFilterChain

  • 本质是通过递归调用 filter(exchange, chain) 来串联执行。

public Mono<Void> filter(ServerWebExchange exchange) {
    return filter0(0, exchange);
}

private Mono<Void> filter0(int index, ServerWebExchange exchange) {
    if (index == filters.size()) {
        return Mono.empty(); // 结束链
    }
    GatewayFilter filter = filters.get(index);
    return filter.filter(exchange, e -> filter0(index + 1, exchange));
}

4. 过滤器类型与排序机制

两类过滤器:

类型 接口 应用范围
全局过滤器 GlobalFilter 所有请求
局部过滤器 GatewayFilterFactory 匹配路由

排序机制(统一使用 Spring 的 Ordered 接口)

  • 过滤器最终封装为 OrderedGatewayFilter,具有 getOrder() 值。

  • 数字越小优先级越高,先执行

  • 默认值参考:

    • NettyRoutingFilter: Ordered.LOWEST_PRECEDENCE(最后执行,实际转发)

    • LoadBalancerClientFilter: 10150

    • RemoveRequestHeaderFilter: 1


5. 过滤器执行阶段划分

每个过滤器可以分为两个阶段:

阶段 方法位置 功能说明
前置处理 filter(exchange, chain) 方法前部逻辑 修改请求、日志、校验、安全等
后置处理 chain.filter(exchange).then(...) 记录响应日志、处理响应内容等

示例:

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    log.info("前置逻辑");
    return chain.filter(exchange)
                .then(Mono.fromRunnable(() -> {
                    log.info("后置逻辑");
                }));
}

6. 断言 vs 过滤器的执行顺序对比

执行组件 执行阶段 是否参与实际请求处理 是否可修改请求 是否参与响应处理
Predicate 路由匹配前 ❌(只判断,不处理) ✅(间接地)
Filter 路由匹配后

✅ 总结顺序:

[1] 路由列表加载
[2] Predicate 按序执行判断是否命中(所有断言 AND 关系)
[3] 命中后构造 GatewayFilterChain(全局 + 路由)
[4] 按 Ordered 排序执行 filter(责任链递归调用)

7. 实战示意图

+-------------------+
| Client Request    |
+-------------------+
        ↓
[RoutePredicateHandlerMapping]
        ↓
[Predicate1] → true
[Predicate2] → true
        ↓ 命中路由
  ↓ 构造 Ordered GatewayFilterChain
        ↓
→ GlobalFilter1 (order=1)
→ GatewayFilterA (order=5)
→ GatewayFilterB (order=10)
→ LoadBalancerClientFilter (order=10150)
→ NettyRoutingFilter (order=LOWEST)
        ↓
[发起 HTTP 请求]
        ↓
[响应处理 → 倒序执行后置逻辑]

8. 调试建议

目的 调试方式
查看是否匹配路由 打印路由断言执行日志或开启 debug 日志
查看过滤器执行顺序 自定义过滤器实现 Ordered,打日志确认链执行顺序
排查过滤器不生效 检查是否被早期过滤器 short-circuit 或未加到 route 中

9. 补充:Spring Gateway 源码关键类

功能 类名
路由匹配 RoutePredicateHandlerMapping
断言执行 AbstractRoutePredicateFactory 子类
路由构建 RouteLocator, CachingRouteLocator
全局过滤器 GlobalFilter 接口
路由过滤器 GatewayFilterFactory, GatewayFilter
过滤器执行链 GatewayFilterChain, OrderedGatewayFilter

四、自定义断言和过滤器的实现

1. 自定义断言(Route Predicate)

1️⃣ 断言作用

断言用于匹配路由规则,只有当断言通过时,路由才会被选中。


2️⃣ 实现步骤

Step 1:创建断言工厂类

必须继承:

AbstractRoutePredicateFactory<YourConfig>
示例:只有在早上9点后才允许路由生效
@Component
public class TimeAfterRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeAfterRoutePredicateFactory.Config> {

    public TimeAfterRoutePredicateFactory() {
        super(Config.class);
    }

    public static class Config {
        private LocalTime after;

        public LocalTime getAfter() { return after; }
        public void setAfter(LocalTime after) { this.after = after; }
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            LocalTime now = LocalTime.now();
            return now.isAfter(config.getAfter());
        };
    }

    // 可选,用于 YAML 参数绑定顺序
    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("after");
    }
}

3️⃣ 使用配置方式

application.yml 中:

spring:
  cloud:
    gateway:
      routes:
        - id: time-limited-route
          uri: http://localhost:8081
          predicates:
            - Path=/time/**
            - TimeAfter=09:00:00

⚠️ 注意:

  • TimeAfter 对应的是工厂类名去掉 RoutePredicateFactory 后的部分。

  • 参数顺序来自 shortcutFieldOrder() 方法。


2. 自定义过滤器(GatewayFilter)

1️⃣ 过滤器作用

用于增强请求处理逻辑:鉴权、日志、限流、重写路径、头部处理等。


2️⃣ 实现步骤

Step 1:创建过滤器工厂类

继承:

AbstractGatewayFilterFactory<YourConfig>
示例:记录请求日志
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    public LogGatewayFilterFactory() {
        super(Config.class);
    }

    public static class Config {
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;

        // Getter/Setter
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("baseMessage", "preLogger", "postLogger");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (config.preLogger) {
                System.out.println("[PRE] " + config.baseMessage + " 请求路径: " + exchange.getRequest().getURI());
            }

            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if (config.postLogger) {
                    System.out.println("[POST] 响应码: " + exchange.getResponse().getStatusCode());
                }
            }));
        };
    }
}

3️⃣ 使用配置方式

application.yml 中:

spring:
  cloud:
    gateway:
      routes:
        - id: log-filter-route
          uri: http://localhost:8081
          predicates:
            - Path=/log/**
          filters:
            - Log=访问日志,true,true

解释:

  • Log 对应类名 LogGatewayFilterFactory

  • 配置参数顺序由 shortcutFieldOrder 决定


3. 执行顺序说明

  • 路由 → Predicate 判断是否命中 → 构建 GatewayFilterChain

  • GatewayFilterChain 按 Ordered 顺序执行

  • 自定义过滤器可以通过实现 OrderedGatewayFilter 指定优先级

示例(设置顺序):

return new OrderedGatewayFilter((exchange, chain) -> {
    // filter logic
}, 10); // 优先级为 10

4. 调试技巧

目的 方法
确认断言是否生效 在断言中加日志打印 / 使用 Spring Boot DevTools
确认过滤器执行顺序 打印日志 + 设置 Ordered 显式顺序
参数绑定失败 检查 shortcutFieldOrder() 参数名一致性
多个过滤器调试 用不同前缀标识并组合多个过滤器调试执行链

5. 小结对比

维度 断言(Predicate) 过滤器(Filter)
用途 路由选择条件 请求/响应增强逻辑
执行时机 在请求处理链构建之前 在请求处理链中间
扩展方式 继承 AbstractRoutePredicateFactory 继承 AbstractGatewayFilterFactory
配置方式 predicates: - CustomName=val1,... filters: - CustomName=val1,...
控制顺序 不支持顺序控制(都是 AND) 支持顺序,需使用 OrderedGatewayFilter
示例用途 限定时间、Header、Method、IP 白名单等 鉴权、加解密、日志记录、限流、路径重写、CORS 等等

五、自定义过滤器+断言组合成拦截器链

1. 核心理念:拦截器链的行为建模

在 Gateway 中:

拦截器功能 对应概念 特点
匹配请求 断言(Predicate) 用于路由匹配条件,匹配失败就跳过
拦截处理 过滤器(GatewayFilter) 支持链式处理、前置、后置逻辑

它们组合使用时形成如下拦截流程:

客户端请求
   ↓
全局过滤器(GlobalFilter)
   ↓
断言1 → 不满足则跳过当前Route
断言2 → 不满足则跳过当前Route
...
满足所有断言(命中路由)
   ↓
GatewayFilter1 → GatewayFilter2 → ...
   ↓
目标服务(URI)
   ↓
响应时倒序执行后置逻辑

2. 自定义断言 + 自定义过滤器组合示例

目标:只允许在上午时间段访问 /secure/** 接口,并打印日志、添加请求头。


1️⃣ 自定义断言:TimeRangeRoutePredicateFactory

@Component
public class TimeRangeRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeRangeRoutePredicateFactory.Config> {

    public TimeRangeRoutePredicateFactory() {
        super(Config.class);
    }

    public static class Config {
        private LocalTime from;
        private LocalTime to;

        public LocalTime getFrom() { return from; }
        public void setFrom(LocalTime from) { this.from = from; }

        public LocalTime getTo() { return to; }
        public void setTo(LocalTime to) { this.to = to; }
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            LocalTime now = LocalTime.now();
            return now.isAfter(config.getFrom()) && now.isBefore(config.getTo());
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("from", "to");
    }
}

2️⃣ 自定义过滤器:AddHeaderAndLogGatewayFilterFactory

@Component
public class AddHeaderAndLogGatewayFilterFactory extends AbstractGatewayFilterFactory<AddHeaderAndLogGatewayFilterFactory.Config> {

    public AddHeaderAndLogGatewayFilterFactory() {
        super(Config.class);
    }

    public static class Config {
        private String headerName;
        private String headerValue;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("headerName", "headerValue");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            System.out.println("[前置] 添加Header: " + config.headerName);
            exchange.getRequest().mutate()
                .header(config.headerName, config.headerValue)
                .build();
            return chain.filter(exchange).then(
                Mono.fromRunnable(() -> System.out.println("[后置] 响应完成"))
            );
        };
    }
}

3️⃣ 配置使用(application.yml

spring:
  cloud:
    gateway:
      routes:
        - id: secure-route
          uri: http://localhost:8081
          predicates:
            - Path=/secure/**
            - TimeRange=08:00,12:00
          filters:
            - AddHeaderAndLog=X-Request-Source,Gateway

3. 执行流程图(拦截器链建模)

请求 /secure/hello
   ↓
断言 Path=/secure/** ✔
断言 TimeRange=08:00-12:00 ✔
   ↓
执行过滤器链:
   - AddHeaderAndLog 前置日志 & 添加请求头
   - 请求转发
   - AddHeaderAndLog 后置日志
   ↓
目标服务响应

4. 高阶扩展建议

功能需求 处理方式
多条件断言组合 自定义组合 Predicate 实现(或多个 predicate 同时配置)
动态参数过滤 支持 SpEL 或读取配置中心值
拦截 + 鉴权 + 路由打标 将断言 + Filter + Header 添加等组合构成完整网关管控链
多个过滤器组合顺序控制 new OrderedGatewayFilter(filter, order) 明确顺序

5. 小结

模块 功能说明 组合效果
断言 判断请求是否应被处理(路由条件) 拦截器链头部条件筛选
过滤器 对匹配的请求执行前后处理逻辑 拦截器链的核心执行流程
组合 条件满足 → 执行链式请求增强 类似 Spring MVC 的拦截器链

六、实现基于 Token 的认证逻辑

在 Spring Cloud Gateway 中实现基于 Token 的认证逻辑,核心思路是:
使用**自定义过滤器(GatewayFilter)**对请求进行拦截,提取 Token → 验签/解析 → 决定是否放行

场景目标

网关层实现对所有请求的 Token 验证,校验失败直接返回 401,无需进入下游服务。


1. 技术选型

  • ✅ 使用 JWT(JSON Web Token) 作为 Token 格式

  • ✅ 自定义 GatewayFilter 拦截请求,校验 JWT 合法性

  • ✅ 校验通过 → 继续路由

  • ✅ 校验失败 → 返回统一错误响应


2. Token 示例(JWT)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyMSIsImlhdCI6MTY5MjAwMDAwMCwiZXhwIjoxNjkyMDAzNjAwfQ.
Nf8cnRku7k7lDghnK8kluhXbZ1bIsvPrKjD7v4-HqDU

3. 自定义 Token 过滤器实现

🔧 1. JWT 工具类(可使用 jjwt

public class JwtUtil {
    private static final String SECRET_KEY = "my-secret";

    public static Claims parseToken(String token) throws JwtException {
        return Jwts.parser()
            .setSigningKey(SECRET_KEY.getBytes(StandardCharsets.UTF_8))
            .parseClaimsJws(token)
            .getBody();
    }
}

🔐 2. 自定义过滤器 TokenAuthGatewayFilterFactory

@Component
public class TokenAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenAuthGatewayFilterFactory.Config> {

    public TokenAuthGatewayFilterFactory() {
        super(Config.class);
    }

    public static class Config {
        private boolean enabled;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("enabled");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.enabled) {
                return chain.filter(exchange);
            }

            ServerHttpRequest request = exchange.getRequest();
            String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

            if (token == null || !token.startsWith("Bearer ")) {
                return unauthorized(exchange, "Token missing or malformed");
            }

            try {
                Claims claims = JwtUtil.parseToken(token.replace("Bearer ", ""));
                // 可将用户信息写入 Header 或请求属性中
                request = exchange.getRequest().mutate()
                    .header("X-User-Id", claims.getSubject())
                    .build();
                return chain.filter(exchange.mutate().request(request).build());

            } catch (JwtException e) {
                return unauthorized(exchange, "Token invalid: " + e.getMessage());
            }
        };
    }

    private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        byte[] bytes = ("{\"error\": \"" + message + "\"}").getBytes(StandardCharsets.UTF_8);
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
            .bufferFactory().wrap(bytes)));
    }
}

4. 配置使用示例 application.yml

spring:
  cloud:
    gateway:
      routes:
        - id: secure-api
          uri: http://localhost:8081
          predicates:
            - Path=/secure/**
          filters:
            - TokenAuth=true

5. 认证流程图

请求 /secure/hello
 → 进入 Gateway
   → 匹配断言 Path=/secure/**
   → 执行过滤器 TokenAuth
       → 从 Header 中提取 Bearer Token
       → 校验签名 & 有效期
       → 失败返回 401
       → 成功添加用户信息继续路由
 → 路由到后端服务

6. Token 认证增强建议(可选)

需求 实现建议
白名单路径免认证 配置 if 条件跳过部分 Path
支持 Redis Token 存储 Token 可存 Redis + 校验时检查有效性(如登出或禁用)
支持权限角色检查 claims 中附带 roles → 写入 Header → 后端根据角色判定权限
动态开关鉴权 application.yml 配置 flag 或使用 Nacos 动态刷新
统一异常处理 & 返回结构 配合 GlobalFilter 实现通用异常响应包装逻辑

7. 测试示例

curl -H "Authorization: Bearer <your-token>" http://localhost:8080/secure/hello

8. 依赖库参考

pom.xml 中添加:

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>

9. 小结

项目 实现组件
鉴权入口 自定义 GatewayFilter
Token 格式 JWT,签名加密后传递
鉴权失败响应 自定义 401 输出
用户透传 将用户信息加入 header 传递下游
可组合性 可与断言组合使用,形成安全控制链

网站公告

今日签到

点亮在社区的每一天
去签到