Skip to content

AddRequestHeader

概述

AddRequestHeader 用于在请求转发到下游服务之前向请求头中添加自定义的头信息。这个过滤器在微服务架构中非常有用,可以帮助我们在网关层统一添加认证信息、追踪标识、业务标识等头信息。

业务场景

在实际的微服务架构中,AddRequestHeader 过滤器常用于以下场景:

  1. 统一认证信息传递:在网关层添加用户身份信息,避免每个微服务都要处理认证逻辑
  2. 链路追踪:添加请求追踪 ID,方便在分布式系统中追踪请求链路
  3. 业务标识传递:添加租户 ID、版本号等业务相关的头信息
  4. API 版本控制:为不同的路由添加版本标识

基本配置

AddRequestHeader 过滤器工厂接受两个参数:name(头名称)和 value(头值)。

基础使用示例

kotlin
import org.springframework.cloud.gateway.route.RouteLocator
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class GatewayConfig {

    @Bean
    fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("add_request_header_route") { predicateSpec ->
                predicateSpec
                    .path("/api/users/**")  // 匹配用户服务的路径
                    .filters { filterSpec ->
                        filterSpec
                            // 添加请求头:X-Service-Name: user-service
                            .addRequestHeader("X-Service-Name", "user-service")
                            // 添加认证相关的头信息
                            .addRequestHeader("X-Gateway-Auth", "gateway-token-123")
                    }
                    .uri("http://localhost:8081")  // 用户服务地址
            }
            .build()
    }
}
java
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("add_request_header_route", predicateSpec ->
                predicateSpec
                    .path("/api/users/**")  // 匹配用户服务的路径
                    .filters(filterSpec ->
                        filterSpec
                            // 添加请求头:X-Service-Name: user-service
                            .addRequestHeader("X-Service-Name", "user-service")
                            // 添加认证相关的头信息
                            .addRequestHeader("X-Gateway-Auth", "gateway-token-123")
                    )
                    .uri("http://localhost:8081")  // 用户服务地址
            )
            .build();
    }
}

YAML 配置方式

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: https://example.org
          filters:
            # 语法:AddRequestHeader=header名称, header值
            - AddRequestHeader=X-Request-red, blue

这个配置会为所有匹配的请求添加 `X-Request-red: blue` 头信息到下游请求中。

高级功能:URI 变量支持

AddRequestHeader 过滤器支持使用 URI 变量,这些变量会在运行时被实际的路径参数替换。

动态头值示例

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: https://example.org
          predicates:
            - Path=/red/{segment} # 路径变量 segment
          filters:
            # 使用路径变量 {segment} 作为头值的一部分
            - AddRequestHeader=X-Request-Red, Blue-{segment}
kotlin
import org.springframework.cloud.gateway.route.RouteLocator
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class DynamicHeaderConfig {

    @Bean
    fun dynamicHeaderRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            // 用户服务路由 - 传递用户ID
            .route("user_service_route") { predicateSpec ->
                predicateSpec
                    .path("/api/users/{userId}/**")
                    .filters { filterSpec ->
                        filterSpec
                            // 将用户ID添加到请求头中,方便下游服务使用
                            .addRequestHeader("X-User-ID", "{userId}")
                            // 添加当前时间戳
                            .addRequestHeader("X-Request-Timestamp", "${System.currentTimeMillis()}")
                    }
                    .uri("http://user-service:8081")
            }
            // 订单服务路由 - 传递订单ID和用户ID
            .route("order_service_route") { predicateSpec ->
                predicateSpec
                    .path("/api/users/{userId}/orders/{orderId}")
                    .filters { filterSpec ->
                        filterSpec
                            .addRequestHeader("X-User-ID", "{userId}")
                            .addRequestHeader("X-Order-ID", "{orderId}")
                            .addRequestHeader("X-Business-Context", "user-{userId}-order-{orderId}")
                    }
                    .uri("http://order-service:8082")
            }
            .build()
    }
}
java
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DynamicHeaderConfig {

    @Bean
    public RouteLocator dynamicHeaderRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
            // 用户服务路由 - 传递用户ID
            .route("user_service_route", predicateSpec ->
                predicateSpec
                    .path("/api/users/{userId}/**")
                    .filters(filterSpec ->
                        filterSpec
                            // 将用户ID添加到请求头中,方便下游服务使用
                            .addRequestHeader("X-User-ID", "{userId}")
                            // 添加当前时间戳
                            .addRequestHeader("X-Request-Timestamp", String.valueOf(System.currentTimeMillis()))
                    )
                    .uri("http://user-service:8081")
            )
            // 订单服务路由 - 传递订单ID和用户ID
            .route("order_service_route", predicateSpec ->
                predicateSpec
                    .path("/api/users/{userId}/orders/{orderId}")
                    .filters(filterSpec ->
                        filterSpec
                            .addRequestHeader("X-User-ID", "{userId}")
                            .addRequestHeader("X-Order-ID", "{orderId}")
                            .addRequestHeader("X-Business-Context", "user-{userId}-order-{orderId}")
                    )
                    .uri("http://order-service:8082")
            )
            .build();
    }
}

实战案例

Details

微服务认证信息传递

在微服务架构中,我们经常需要在网关层进行统一认证,然后将认证信息传递给下游服务。

kotlin
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
import org.springframework.http.HttpHeaders
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange

@Component
class AuthGatewayFilter : AbstractGatewayFilterFactory<AuthGatewayFilter.Config>() {

    override fun apply(config: Config): GatewayFilter {
        return GatewayFilter { exchange, chain ->
            val request = exchange.request
            val headers = request.headers

            // 获取认证Token
            val authToken = headers.getFirst(HttpHeaders.AUTHORIZATION)

            if (!authToken.isNullOrEmpty()) {
                // 解析Token获取用户信息(这里简化处理)
                val userId = extractUserIdFromToken(authToken)
                val userRole = extractUserRoleFromToken(authToken)

                // 构建新的请求,添加用户信息到请求头
                val mutatedRequest = request.mutate()
                    .header("X-User-ID", userId)
                    .header("X-User-Role", userRole)
                    .header("X-Auth-Source", "gateway")
                    .build()

                val mutatedExchange = exchange.mutate()
                    .request(mutatedRequest)
                    .build()

                chain.filter(mutatedExchange)
            } else {
                chain.filter(exchange)
            }
        }
    }

    /**
     * 从Token中提取用户ID(实际项目中需要使用JWT解析)
     */
    private fun extractUserIdFromToken(token: String): String {
        // 这里只是示例,实际应该解析JWT Token
        return "user-12345"
    }

    /**
     * 从Token中提取用户角色
     */
    private fun extractUserRoleFromToken(token: String): String {
        // 这里只是示例,实际应该解析JWT Token
        return "ROLE_USER"
    }

    override fun getConfigClass(): Class<Config> = Config::class.java

    class Config {
        // 配置类,可以添加一些配置参数
    }
}
java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            var request = exchange.getRequest();
            var headers = request.getHeaders();

            // 获取认证Token
            String authToken = headers.getFirst(HttpHeaders.AUTHORIZATION);

            if (authToken != null && !authToken.isEmpty()) {
                // 解析Token获取用户信息(这里简化处理)
                String userId = extractUserIdFromToken(authToken);
                String userRole = extractUserRoleFromToken(authToken);

                // 构建新的请求,添加用户信息到请求头
                var mutatedRequest = request.mutate()
                    .header("X-User-ID", userId)
                    .header("X-User-Role", userRole)
                    .header("X-Auth-Source", "gateway")
                    .build();

                var mutatedExchange = exchange.mutate()
                    .request(mutatedRequest)
                    .build();

                return chain.filter(mutatedExchange);
            } else {
                return chain.filter(exchange);
            }
        };
    }

    /**
     * 从Token中提取用户ID(实际项目中需要使用JWT解析)
     */
    private String extractUserIdFromToken(String token) {
        // 这里只是示例,实际应该解析JWT Token
        return "user-12345";
    }

    /**
     * 从Token中提取用户角色
     */
    private String extractUserRoleFromToken(String token) {
        // 这里只是示例,实际应该解析JWT Token
        return "ROLE_USER";
    }

    @Override
    public Class<Config> getConfigClass() {
        return Config.class;
    }

    public static class Config {
        // 配置类,可以添加一些配置参数
    }
}
Details

链路追踪 ID 传递

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tracing_route
          uri: http://downstream-service
          predicates:
            - Path=/api/**
          filters:
            # 添加链路追踪ID
            - AddRequestHeader=X-Trace-ID, ${random.uuid}
            # 添加网关标识
            - AddRequestHeader=X-Gateway-Instance, gateway-001
            # 添加请求时间戳
            - AddRequestHeader=X-Request-Time, ${T(java.time.Instant).now()}
Details

与其他过滤器组合使用,构建更复杂的网关功能

kotlin
import org.springframework.cloud.gateway.route.RouteLocator
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class CombinedFiltersConfig {

    @Bean
    fun combinedFiltersRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("api_route") { predicateSpec ->
                predicateSpec
                    .path("/api/**")
                    .filters { filterSpec ->
                        filterSpec
                            // 1. 限流过滤器
                            .requestRateLimiter { rateLimiterSpec ->
                                rateLimiterSpec
                                    .rateLimiter { redisRateLimiter ->
                                        redisRateLimiter
                                            .replenishRate(10)      // 每秒允许10个请求
                                            .burstCapacity(20)      // 突发容量20
                                            .requestedTokens(1)     // 每个请求消耗1个令牌
                                    }
                                    .keyResolver { exchange ->
                                        // 基于客户端IP进行限流
                                        reactor.core.publisher.Mono.just(
                                            exchange.request.remoteAddress?.address?.hostAddress ?: "unknown"
                                        )
                                    }
                            }
                            // 2. 添加请求头信息
                            .addRequestHeader("X-Rate-Limited", "true")
                            .addRequestHeader("X-Client-IP", "#{T(java.net.InetAddress).getLocalHost().getHostAddress()}")
                            // 3. 重写路径
                            .rewritePath("/api/(?<segment>.*)", "/v1/\${segment}")
                            // 4. 添加响应头
                            .addResponseHeader("X-Gateway-Response", "processed")
                    }
                    .uri("http://backend-service:8080")
            }
            .build()
    }
}
java
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CombinedFiltersConfig {

    @Bean
    public RouteLocator combinedFiltersRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("api_route", predicateSpec ->
                predicateSpec
                    .path("/api/**")
                    .filters(filterSpec ->
                        filterSpec
                            // 1. 限流过滤器
                            .requestRateLimiter(rateLimiterSpec ->
                                rateLimiterSpec
                                    .rateLimiter(redisRateLimiter ->
                                        redisRateLimiter
                                            .replenishRate(10)      // 每秒允许10个请求
                                            .burstCapacity(20)      // 突发容量20
                                            .requestedTokens(1)     // 每个请求消耗1个令牌
                                    )
                                    .keyResolver(exchange ->
                                        // 基于客户端IP进行限流
                                        reactor.core.publisher.Mono.just(
                                            exchange.getRequest().getRemoteAddress() != null ?
                                                exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() :
                                                "unknown"
                                        )
                                    )
                            )
                            // 2. 添加请求头信息
                            .addRequestHeader("X-Rate-Limited", "true")
                            .addRequestHeader("X-Client-IP", "#{T(java.net.InetAddress).getLocalHost().getHostAddress()}")
                            // 3. 重写路径
                            .rewritePath("/api/(?<segment>.*)", "/v1/${segment}")
                            // 4. 添加响应头
                            .addResponseHeader("X-Gateway-Response", "processed")
                    )
                    .uri("http://backend-service:8080")
            )
            .build();
    }
}

最佳实践

1. 头信息命名规范

建议使用统一的前缀来标识网关添加的头信息,如 `X-Gateway-`、`X-Custom-` 等,这样便于区分和管理。

kotlin
// 推荐的头信息命名
.addRequestHeader("X-Gateway-User-ID", "{userId}")
.addRequestHeader("X-Gateway-Timestamp", "${System.currentTimeMillis()}")
.addRequestHeader("X-Gateway-Version", "v1.0")

// 不推荐的命名
.addRequestHeader("user", "{userId}")          // 太简单,可能冲突
.addRequestHeader("id", "{userId}")            // 语义不明确

2. 避免敏感信息泄露

不要在请求头中传递密码、私钥等敏感信息。如果需要传递认证信息,建议使用加密的 Token。

kotlin
// ❌ 错误做法 - 直接传递敏感信息
.addRequestHeader("X-User-Password", "plain-password")

// ✅ 正确做法 - 传递加密的Token或ID
.addRequestHeader("X-User-Token", "encrypted-jwt-token")
.addRequestHeader("X-User-ID", "{userId}")

3. 性能考虑

避免在请求头中添加过大的数据,这会影响网络传输性能。如果需要传递大量数据,考虑使用缓存或数据库。

常见问题与解决方案

问题 1:头信息未生效

问题现象:配置了 AddRequestHeader 但下游服务接收不到头信息。

可能原因

  1. 过滤器配置语法错误
  2. 路由匹配条件不正确
  3. 下游服务获取头信息的方式不正确

解决方案

kotlin
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
import org.springframework.stereotype.Component
import org.slf4j.LoggerFactory

@Component
class DebugHeaderFilter : AbstractGatewayFilterFactory<DebugHeaderFilter.Config>() {

    private val logger = LoggerFactory.getLogger(DebugHeaderFilter::class.java)

    override fun apply(config: Config): GatewayFilter {
        return GatewayFilter { exchange, chain ->
            val request = exchange.request

            // 打印所有请求头信息,用于调试
            logger.info("=== 请求头信息 ===")
            request.headers.forEach { (name, values) ->
                logger.info("Header: $name = $values")
            }
            logger.info("=== 请求头信息结束 ===")

            chain.filter(exchange)
        }
    }

    override fun getConfigClass(): Class<Config> = Config::class.java

    class Config
}
java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

    private static final Logger logger = LoggerFactory.getLogger(DebugHeaderFilter.class);

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            var request = exchange.getRequest();

            // 打印所有请求头信息,用于调试
            logger.info("=== 请求头信息 ===");
            request.getHeaders().forEach((name, values) -> {
                logger.info("Header: {} = {}", name, values);
            });
            logger.info("=== 请求头信息结束 ===");

            return chain.filter(exchange);
        };
    }

    @Override
    public Class<Config> getConfigClass() {
        return Config.class;
    }

    public static class Config {
    }
}

问题 2:URI 变量未正确替换

问题现象:使用了 {变量名} 语法,但实际请求头中显示的还是字面量 {变量名}

解决方案:确保路径谓词正确匹配,并且变量名称一致。

yaml
# ❌ 错误配置 - 变量名不匹配
spring:
  cloud:
    gateway:
      routes:
      - id: wrong_variable_route
        predicates:
        - Path=/users/{userId}/**
        filters:
        - AddRequestHeader=X-User-ID, {userID}  # 注意这里是 userID,与路径中的 userId 不匹配

# ✅ 正确配置 - 变量名匹配
spring:
  cloud:
    gateway:
      routes:
      - id: correct_variable_route
        predicates:
        - Path=/users/{userId}/**
        filters:
        - AddRequestHeader=X-User-ID, {userId}  # 变量名与路径中的一致

总结

AddRequestHeader 过滤器工厂是 Spring Cloud Gateway 中一个简单但非常实用的功能组件。通过合理使用这个过滤器,我们可以:

  1. 统一处理认证信息:在网关层添加用户身份、权限等信息
  2. 实现链路追踪:添加追踪 ID,便于分布式系统的问题排查
  3. 传递业务上下文:将路径参数、业务标识等信息传递给下游服务
  4. 实现 API 版本控制:为不同版本的 API 添加版本标识

在实际使用中,建议结合日志、监控等手段,确保头信息正确传递,并注意性能和安全方面的考虑。通过合理的配置和最佳实践,`AddRequestHeader` 可以大大简化微服务间的信息传递逻辑。