如何使用Spring Cloud Gateway自定义过滤器来过滤每个请求?

huangapple go评论108阅读模式
英文:

How to use a Spring Cloud Gateway Custom Filter to filter every request?

问题

  1. 这是我第一次进行Spring Cloud Gateway实现。
  2. 我需要过滤每个请求并在某些路径上应用过滤器验证。根据[Baeldung自定义过滤器教程][1],我创建了一个简单的应用程序来过滤请求。
  3. 该应用程序必须释放像`/actuator/health`这样的路径,并验证指定的路径到后端服务。到目前为止,我已经实现了一个`GlobalFilter`和一个`GatewayFilterFactory`。全局过滤器在每个请求时都会被调用,但是GatewayFilter只在应用程序启动时调用一次,因此我无法为每个请求执行身份验证逻辑。身份验证逻辑涉及特定的标头字段。因此,我关注的问题是:
  4. 1. 如何对每个请求进行特定路径的验证?
  5. 2. 如何拒绝请求并发送错误消息?
  6. **GlobalFilter**
  7. ```java
  8. @Component
  9. public class LoggingGlobalPreFilter implements GlobalFilter {
  10. final Logger LOGGER = LoggerFactory.getLogger(LoggingGlobalPreFilter.class);
  11. @Override
  12. public Mono<Void> filter(
  13. ServerWebExchange exchange,
  14. GatewayFilterChain chain) {
  15. LOGGER.info("Global Pre Filter executed");
  16. return chain.filter(exchange);
  17. }
  18. }

GatewayFilter

  1. @Component
  2. public class LoggingGatewayFilterFactory extends
  3. AbstractGatewayFilterFactory<LoggingGatewayFilterFactory.Config> {
  4. final Logger LOGGER =
  5. LoggerFactory.getLogger(LoggingGatewayFilterFactory.class);
  6. public LoggingGatewayFilterFactory() {
  7. super(Config.class);
  8. }
  9. private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
  10. ServerHttpResponse response = exchange.getResponse();
  11. response.setStatusCode(httpStatus);
  12. return response.setComplete();
  13. }
  14. private boolean isAuthorizationValid(String authorizationHeader) {
  15. boolean isValid = true;
  16. return authorizationHeader.equals("x-header");
  17. }
  18. @Override
  19. public GatewayFilter apply(Config config) {
  20. LOGGER.info("M=apply, Msg=Applying Gateway Filter....");
  21. return ((exchange, chain) -> {
  22. LOGGER.info("M=apply, Msg=Applying Gateway Filter...."); // 显然从未进入此处。
  23. ServerHttpRequest request = exchange.getRequest();
  24. if (!request.getHeaders().containsKey(TsApiGatewayConstants.HEADER_APIKEY)) {
  25. return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_MISSING, HttpStatus.UNAUTHORIZED);
  26. }
  27. String apiKey = request.getHeaders().get(TsApiGatewayConstants.HEADER_APIKEY).get(0);
  28. String userAgent = request.getHeaders().get(TsApiGatewayConstants.HEADER_USER_AGENT).get(0);
  29. if (!this.isAuthorizationValid(userAgent)) {
  30. return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_INVALID, HttpStatus.UNAUTHORIZED);
  31. }
  32. return chain.filter(exchange);
  33. });
  34. }
  35. public static class Config {
  36. private String baseMessage;
  37. private boolean preLogger;
  38. private boolean postLogger;
  39. public Config(String baseMessage, boolean preLogger, boolean postLogger) {
  40. this.baseMessage = baseMessage;
  41. this.preLogger = preLogger;
  42. this.postLogger = postLogger;
  43. }
  44. public String getBaseMessage() {
  45. return baseMessage;
  46. }
  47. public void setBaseMessage(String baseMessage) {
  48. this.baseMessage = baseMessage;
  49. }
  50. public boolean isPreLogger() {
  51. return preLogger;
  52. }
  53. public void setPreLogger(boolean preLogger) {
  54. this.preLogger = preLogger;
  55. }
  56. public boolean isPostLogger() {
  57. return postLogger;
  58. }
  59. public void setPostLogger(boolean postLogger) {
  60. this.postLogger = postLogger;
  61. }
  62. }
  63. }

application.yml

  1. cloud:
  2. gateway:
  3. routes:
  4. - id: service_route
  5. uri: https://backend-url:443
  6. predicates:
  7. - Path=/api
  8. filters:
  9. - Logging

示例要过滤的路径:https://backend-url:443/api/service1

  1. <details>
  2. <summary>英文:</summary>
  3. It&#39;s my first time at Spring Cloud Gateway implementation.
  4. 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.
  5. The application must release paths like `/actuator/health` and validate specific paths to backend service. So far, I&#39;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&#39;t make the auth logic to every request. The auth logic is about a specific header field. So, my grained questions are:
  6. 1. How validate every request with a specific path?
  7. 2. How refuse a request and send a error message?
  8. **GlobalFilter**
  9. @Component
  10. public class LoggingGlobalPreFilter implements GlobalFilter {
  11. final Logger LOGGER = LoggerFactory.getLogger(LoggingGlobalPreFilter.class);
  12. @Override
  13. public Mono&lt;Void&gt; filter(
  14. ServerWebExchange exchange,
  15. GatewayFilterChain chain) {
  16. LOGGER.info(&quot;Global Pre Filter executed&quot;);
  17. return chain.filter(exchange);
  18. }
  19. }
  20. **GatewayFilter**
  21. @Component
  22. public class LoggingGatewayFilterFactory extends
  23. AbstractGatewayFilterFactory&lt;LoggingGatewayFilterFactory.Config&gt; {
  24. final Logger LOGGER =
  25. LoggerFactory.getLogger(LoggingGatewayFilterFactory.class);
  26. public LoggingGatewayFilterFactory() {
  27. super(Config.class);
  28. }
  29. private Mono&lt;Void&gt; onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
  30. ServerHttpResponse response = exchange.getResponse();
  31. response.setStatusCode(httpStatus);
  32. return response.setComplete();
  33. }
  34. private boolean isAuthorizationValid(String authorizationHeader) {
  35. boolean isValid = true;
  36. return authorizationHeader.equals(&quot;x-header&quot;);
  37. }
  38. @Override
  39. public GatewayFilter apply(Config config) {
  40. LOGGER.info(&quot;M=apply, Msg=Applying Gateway Filter....&quot;);
  41. return ((exchange, chain) -&gt; {
  42. LOGGER.info(&quot;M=apply, Msg=Applying Gateway Filter....&quot;); // APARENTELLY NEVER ENTER HERE.
  43. ServerHttpRequest request = exchange.getRequest();
  44. if (!request.getHeaders().containsKey(TsApiGatewayConstants.HEADER_APIKEY)) {
  45. return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_MISSING, HttpStatus.UNAUTHORIZED);
  46. }
  47. String apiKey = request.getHeaders().get(TsApiGatewayConstants.HEADER_APIKEY).get(0);
  48. String userAgent = request.getHeaders().get(TsApiGatewayConstants.HEADER_USER_AGENT).get(0);
  49. if (!this.isAuthorizationValid(userAgent)) {
  50. return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_INVALID, HttpStatus.UNAUTHORIZED);
  51. }
  52. return chain.filter(exchange);
  53. });
  54. }
  55. public static class Config {
  56. private String baseMessage;
  57. private boolean preLogger;
  58. private boolean postLogger;
  59. public Config(String baseMessage, boolean preLogger, boolean postLogger) {
  60. this.baseMessage = baseMessage;
  61. this.preLogger = preLogger;
  62. this.postLogger = postLogger;
  63. }
  64. public String getBaseMessage() {
  65. return baseMessage;
  66. }
  67. public void setBaseMessage(String baseMessage) {
  68. this.baseMessage = baseMessage;
  69. }
  70. public boolean isPreLogger() {
  71. return preLogger;
  72. }
  73. public void setPreLogger(boolean preLogger) {
  74. this.preLogger = preLogger;
  75. }
  76. public boolean isPostLogger() {
  77. return postLogger;
  78. }
  79. public void setPostLogger(boolean postLogger) {
  80. this.postLogger = postLogger;
  81. }
  82. }
  83. }
  84. **application.yml**
  85. cloud:
  86. gateway:
  87. routes:
  88. - id: service_route
  89. uri: https://backend-url:443
  90. predicates:
  91. - Path=/api
  92. filters:
  93. - Logging
  94. Example path to filter: `https://backend-url:443/api/service1`
  95. [1]: https://www.baeldung.com/spring-cloud-custom-gateway-filters
  96. </details>
  97. # 答案1
  98. **得分**: 6
  99. 我已找到解决方法。我使用了RouteConfiguration组件来设置路由,以及一个GatewayFilter类。在RouteConfiguration的Bean中,我将特定的过滤器设置到了路由路径上。在我的情况下,我使用了一个过滤器来进行身份验证。
  100. **GatewayFilter**
  101. ```java
  102. @RefreshScope
  103. @Component
  104. public class AuthenticationFilter implements GatewayFilter {
  105. final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
  106. @Override
  107. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  108. ServerHttpRequest request = exchange.getRequest();
  109. // 进行您的业务逻辑,这是一个简单的示例。
  110. if (!request.getHeaders().containsKey("x-api-key")) {
  111. return this.onError(exchange, "api-key missing", HttpStatus.FORBIDDEN);
  112. }
  113. return chain.filter(exchange); // 转发到路由
  114. }
  115. private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
  116. ServerHttpResponse response = exchange.getResponse();
  117. response.setStatusCode(httpStatus);
  118. return response.setComplete();
  119. }
  120. }

RouteConfiguration

  1. @RefreshScope
  2. @Configuration
  3. public class RouteConfiguration {
  4. @Value("${routes.api}")
  5. private String apiHost;
  6. @Autowired
  7. AuthenticationFilter authenticationFilter;
  8. @Bean
  9. public RouteLocator apiRoutes(RouteLocatorBuilder builder) {
  10. return builder.routes()
  11. .route("CHOICE A ROUTE ID", p -> p
  12. .path("/api/**")
  13. .filters(f -> f
  14. .filter(authenticationFilter) // 您可以在这里添加更多的过滤器。
  15. .stripPrefix(1))
  16. .uri(apiHost))
  17. .build();
  18. }
  19. }
英文:

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

  1. @RefreshScope
  2. @Component
  3. public class AuthenticationFilter implements GatewayFilter {
  4. final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
  5. @Override
  6. public Mono&lt;Void&gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  7. ServerHttpRequest request = exchange.getRequest();
  8. // Make your business logic, this is a simple sample.
  9. if (!request.getHeaders().containsKey(&quot;x-api-key&quot;)) {
  10. return this.onError(exchange,&quot;api-key missing&quot;,HttpStatus.FORBIDDEN);
  11. }
  12. return chain.filter(exchange); // Forward to route
  13. }
  14. private Mono&lt;Void&gt; onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
  15. ServerHttpResponse response = exchange.getResponse();
  16. response.setStatusCode(httpStatus);
  17. return response.setComplete();
  18. }

RouteConfiguration

  1. @RefreshScope
  2. @Configuration
  3. public class RouteConfiguration {
  4. @Value(&quot;${routes.api}&quot;)
  5. private String apiHost;
  6. @Autowired
  7. AuthenticationFilter authenticationFilter;
  8. @Bean
  9. public RouteLocator apiRoutes(RouteLocatorBuilder builder) {
  10. return builder.routes()
  11. .route(&quot;CHOICE A ROUTE ID&quot;,p -&gt; p
  12. .path(&quot;/api/**&quot;)
  13. .filters(f -&gt; f
  14. .filter(authenticationFilter) // You can add more filters here.
  15. .stripPrefix(1))
  16. .uri(apiHost))
  17. .build();
  18. }
  19. }

答案2

得分: 0

  1. 如果您想要验证每个请求,您应该实现Ordered接口并返回-2
  2. 例如:
  3. @Component
  4. public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
  5. @Override
  6. public int getOrder() {
  7. // -1是响应写入过滤器,必须在其之前调用
  8. return -2;
  9. }
  10. }

拒绝请求并发送错误消息,
您可以查看此方法。使用MonoUtil.buildServerResponse方法。

MonoUtil

  1. <details>
  2. <summary>英文:</summary>
  3. if you want validate every request ,you should impl the Ordered interface and
  4. and return -2;
  5. eg:
  1. @Component

public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -2;
}
}

  1. refuse a request and send a error message
  2. you can see this .use MonoUtil.buildServerResponse method.
  3. [MonoUtil][1]
  4. [1]: https://github.com/haochencheng/microservice-integration/blob/master/microservice-integration-gateway/src/main/java/microservice/integration/gateway/filter/GatewayAdminAuthorizationFilter.java
  5. </details>

huangapple
  • 本文由 发表于 2020年7月25日 04:23:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/63080959.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定