如何编写GatewayFilter将Principal名称作为请求参数传递?
解决方案:Spring Cloud Gateway 动态添加请求参数(从Principal提取clientId)
核心实现逻辑
由于ServerWebExchange不可变、getPrincipal()返回异步类型Mono<Principal>,必须基于响应式编程的链式操作处理:先异步获取Principal,再构建包含新参数的请求实例,最后传递给后续过滤器链。
自定义GatewayFilter代码
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.security.Principal; import java.util.Collections; @Component public class ClientIdRequestParameterFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return exchange.getPrincipal() // 异步获取Principal后,构建带clientId参数的新请求 .map(principal -> { String clientId = principal.getName(); // 生成修改后的请求对象 ServerHttpRequest modifiedRequest = exchange.getRequest().mutate() .queryParams(params -> params.put("clientId", Collections.singletonList(clientId))) .build(); // 生成新的ServerWebExchange(原对象不可变) return exchange.mutate().request(modifiedRequest).build(); }) // 处理Principal不存在的场景,直接返回原exchange .defaultIfEmpty(exchange) // 继续执行后续过滤器链 .flatMap(modifiedExchange -> chain.filter(modifiedExchange)); } }
配置过滤器生效
需要确保该过滤器在认证过滤器之后执行,保证Principal已存在。示例配置类如下:
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 { private final ClientIdRequestParameterFilter clientIdFilter; public GatewayConfig(ClientIdRequestParameterFilter clientIdFilter) { this.clientIdFilter = clientIdFilter; } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("backend_service_route", r -> r.path("/api/**") .filters(f -> f.filter(clientIdFilter)) .uri("http://your-backend-service:8080")) .build(); } }
关键细节说明
- 异步顺序控制:通过
Mono.map()确保先完成Principal的获取和请求修改,再执行后端调用,解决之前逻辑顺序错误的问题。 - 不可变对象处理:利用
mutate()方法创建新的ServerHttpRequest和ServerWebExchange实例,符合Spring WebFlux的设计规范。 - Lambda变量合规:所有变量均为有效final,满足Java Lambda的语法要求。
- 异常兜底:
defaultIfEmpty(exchange)保证认证失败或Principal不存在时,过滤器链仍能正常流转,避免请求中断。
失败原因复盘
- 第一种尝试:未使用响应式链式操作,异步任务的执行顺序失控,导致后端调用先于参数修改完成。
- 第二种尝试:网关自带的
AddRequestParameterGatewayFilterFactory仅支持静态参数注入,无法动态从Principal提取数据,必须自定义过滤器实现。
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

