如何使用 Spring Cloud Gateway 自定义过滤器过滤每个请求?
Posted
技术标签:
【中文标题】如何使用 Spring Cloud Gateway 自定义过滤器过滤每个请求?【英文标题】:How to use a Spring Cloud Gateway Custom Filter to filter every request? 【发布时间】:2020-11-14 18:08:19 【问题描述】:这是我第一次实现 Spring Cloud Gateway。
我需要过滤每个请求并在某些路径上应用过滤器验证。在Baeldung Custom Filters tutorial 之后,我做了一个简单的应用程序来过滤请求。
应用程序必须发布/actuator/health
之类的路径,并验证后端服务的特定路径。到目前为止,我已经实现了GlobalFilter
和GatewayFilterFactory
。每个请求都会调用全局过滤器,但在应用程序启动时只调用一次 GatewayFilter,这样我就无法为每个请求创建身份验证逻辑。 auth 逻辑是关于一个特定的头字段。所以,我的具体问题是:
-
如何使用特定路径验证每个请求?
如何拒绝请求并发送错误信息?
全局过滤器
@Component
public class LoggingGlobalPreFilter implements GlobalFilter
final Logger LOGGER = LoggerFactory.getLogger(LoggingGlobalPreFilter.class);
@Override
public Mono<Void> filter(
ServerWebExchange exchange,
GatewayFilterChain chain)
LOGGER.info("Global Pre Filter executed");
return chain.filter(exchange);
网关过滤器
@Component
public class LoggingGatewayFilterFactory extends
AbstractGatewayFilterFactory<LoggingGatewayFilterFactory.Config>
final Logger LOGGER =
LoggerFactory.getLogger(LoggingGatewayFilterFactory.class);
public LoggingGatewayFilterFactory()
super(Config.class);
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus)
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
private boolean isAuthorizationValid(String authorizationHeader)
boolean isValid = true;
return authorizationHeader.equals("x-header");
@Override
public GatewayFilter apply(Config config)
LOGGER.info("M=apply, Msg=Applying Gateway Filter....");
return ((exchange, chain) ->
LOGGER.info("M=apply, Msg=Applying Gateway Filter...."); // APARENTELLY NEVER ENTER HERE.
ServerHttpRequest request = exchange.getRequest();
if (!request.getHeaders().containsKey(TsApiGatewayConstants.HEADER_APIKEY))
return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_MISSING, HttpStatus.UNAUTHORIZED);
String apiKey = request.getHeaders().get(TsApiGatewayConstants.HEADER_APIKEY).get(0);
String userAgent = request.getHeaders().get(TsApiGatewayConstants.HEADER_USER_AGENT).get(0);
if (!this.isAuthorizationValid(userAgent))
return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_INVALID, HttpStatus.UNAUTHORIZED);
return chain.filter(exchange);
);
public static class Config
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
public Config(String baseMessage, boolean preLogger, boolean postLogger)
this.baseMessage = baseMessage;
this.preLogger = preLogger;
this.postLogger = postLogger;
public String getBaseMessage()
return baseMessage;
public void setBaseMessage(String baseMessage)
this.baseMessage = baseMessage;
public boolean isPreLogger()
return preLogger;
public void setPreLogger(boolean preLogger)
this.preLogger = preLogger;
public boolean isPostLogger()
return postLogger;
public void setPostLogger(boolean postLogger)
this.postLogger = postLogger;
application.yml
cloud:
gateway:
routes:
- id: service_route
uri: https://backend-url:443
predicates:
- Path=/api
filters:
- Logging
过滤器的示例路径:https://backend-url:443/api/service1
【问题讨论】:
我面临同样的问题。我们能解决这个问题吗? @SasukeUchiha 请看下面我的解决方案。 请不要在问题标题中添加SOLVED
之类的内容。如果您的问题已解决,请接受帮助您解决问题的答案,或者 - 如果没有合适的答案 - 发布您自己的答案并在超时后接受。我已恢复将[SOLVED]
添加到标题的编辑。
【参考方案1】:
我找到了解决它的方法。我使用了一个 RouteConfiguration 组件来设置路由和一个 GatewayFilter 类。在 RouteConfiguration 的 Bean 上,我已将特定过滤器设置为路由路径。就我而言,我使用了过滤器进行身份验证。
网关过滤器
@RefreshScope
@Component
public class AuthenticationFilter implements GatewayFilter
final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
ServerHttpRequest request = exchange.getRequest();
// Make your business logic, this is a simple sample.
if (!request.getHeaders().containsKey("x-api-key"))
return this.onError(exchange,"api-key missing",HttpStatus.FORBIDDEN);
return chain.filter(exchange); // Forward to route
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus)
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
路线配置
@RefreshScope
@Configuration
public class RouteConfiguration
@Value("$routes.api")
private String apiHost;
@Autowired
AuthenticationFilter authenticationFilter;
@Bean
public RouteLocator apiRoutes(RouteLocatorBuilder builder)
return builder.routes()
.route("CHOICE A ROUTE ID",p -> p
.path("/api/**")
.filters(f -> f
.filter(authenticationFilter) // You can add more filters here.
.stripPrefix(1))
.uri(apiHost))
.build();
【讨论】:
【参考方案2】:如果你想验证每个请求,你应该实现 Ordered 接口和 并返回 -2; 例如:
@Component
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered
@Override
public int getOrder()
// -1 is response write filter, must be called before that
return -2;
拒绝请求并发送错误消息 你可以看到这个 .use MonoUtil.buildServerResponse 方法。
MonoUtil
【讨论】:
以上是关于如何使用 Spring Cloud Gateway 自定义过滤器过滤每个请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Cloud Gateway 中添加特定于路由的自定义过滤器
如何使用 Spring Cloud Gateway 自定义过滤器过滤每个请求?
如何在提交之前修改 Spring Cloud Gateway 中的响应正文
如何使用Spring Cloud Gateway,99%的人都不知道!
如何在 spring-cloud-gateway 合约测试中从 spring-cloud-contract 中设置带有 StubRunner 端口的 url