英文:
How to use a Spring Cloud Gateway Custom Filter to filter every request?
问题
这是我第一次进行Spring Cloud Gateway实现。
我需要过滤每个请求并在某些路径上应用过滤器验证。根据[Baeldung自定义过滤器教程][1],我创建了一个简单的应用程序来过滤请求。
该应用程序必须释放像`/actuator/health`这样的路径,并验证指定的路径到后端服务。到目前为止,我已经实现了一个`GlobalFilter`和一个`GatewayFilterFactory`。全局过滤器在每个请求时都会被调用,但是GatewayFilter只在应用程序启动时调用一次,因此我无法为每个请求执行身份验证逻辑。身份验证逻辑涉及特定的标头字段。因此,我关注的问题是:
1. 如何对每个请求进行特定路径的验证?
2. 如何拒绝请求并发送错误消息?
**GlobalFilter**
```java
@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);
}
}
GatewayFilter
@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...."); // 显然从未进入此处。
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
<details>
<summary>英文:</summary>
It's my first time at Spring Cloud Gateway implementation.
I need filter every request and apply a filter validation on some paths. Following the [Baeldung Custom Filters tutorial][1] I make a simple application to filter requests.
The application must release paths like `/actuator/health` and validate specific paths to backend service. So far, I've implemented a `GlobalFilter` and a `GatewayFilterFactory`. The Global filter is called every request but the GatewayFilter is called just once when application starts, that way I can't make the auth logic to every request. The auth logic is about a specific header field. So, my grained questions are:
1. How validate every request with a specific path?
2. How refuse a request and send a error message?
**GlobalFilter**
@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);
}
}
**GatewayFilter**
@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
Example path to filter: `https://backend-url:443/api/service1`
[1]: https://www.baeldung.com/spring-cloud-custom-gateway-filters
</details>
# 答案1
**得分**: 6
我已找到解决方法。我使用了RouteConfiguration组件来设置路由,以及一个GatewayFilter类。在RouteConfiguration的Bean中,我将特定的过滤器设置到了路由路径上。在我的情况下,我使用了一个过滤器来进行身份验证。
**GatewayFilter**
```java
@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();
// 进行您的业务逻辑,这是一个简单的示例。
if (!request.getHeaders().containsKey("x-api-key")) {
return this.onError(exchange, "api-key missing", HttpStatus.FORBIDDEN);
}
return chain.filter(exchange); // 转发到路由
}
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
}
RouteConfiguration
@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) // 您可以在这里添加更多的过滤器。
.stripPrefix(1))
.uri(apiHost))
.build();
}
}
英文:
I've found a way to solve it. I've used a RouteConfiguration component to set the routes and a GatewayFilter class. On the RouteConfiguration's Bean I've seted the specific filter to the route path. On my case I've used a filter to make an authentication.
GatewayFilter
@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();
}
RouteConfiguration
@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
得分: 0
如果您想要验证每个请求,您应该实现Ordered接口并返回-2;
例如:
@Component
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// -1是响应写入过滤器,必须在其之前调用
return -2;
}
}
拒绝请求并发送错误消息,
您可以查看此方法。使用MonoUtil.buildServerResponse方法。
<details>
<summary>英文:</summary>
if you want validate every request ,you should impl the Ordered interface and
and return -2;
eg:
@Component
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -2;
}
}
refuse a request and send a error message
you can see this .use MonoUtil.buildServerResponse method.
[MonoUtil][1]
[1]: https://github.com/haochencheng/microservice-integration/blob/master/microservice-integration-gateway/src/main/java/microservice/integration/gateway/filter/GatewayAdminAuthorizationFilter.java
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论