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

标签 java spring spring-cloud spring-cloud-gateway spring-cloud-security

这是我第一次使用 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

    最佳答案

    我找到了解决方法。我使用了 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();
        }
    
    }
    

    关于java - 如何使用 Spring Cloud Gateway 自定义过滤器来过滤每个请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63080959/

    相关文章:

    java - 如何在 Spring Cloud Config 中定义共享数据源

    spring-cloud-sleuth 与 spring-amqp 集成

    java - 处理 ArrayList 并按引用传递

    java - 根据平台切换 Gradle 中的依赖项

    java - 如何通过本地计算机远程连接 Apache 服务器 (http ://149. 4.223.238:8080)

    java - 克隆 Swing 组件中的背景颜色

    spring - 嵌套异常是 java.lang.NoClassDefFoundError : org/hibernate/annotations/common/reflection/ClassLoaderDelegate

    java - 我可以在 Spring Web 应用程序的上下文中打开 REPL 吗?

    java - Spring Cloud Stream Kafka Binder 压缩

    java - 嵌套异常是 java.lang.IncompatibleClassChangeError : org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor