Spring Cloud Gateway拦截所有传入和传出的请求

如何解决Spring Cloud Gateway拦截所有传入和传出的请求

它将是一个后端应用程序,它公开一些REST终结点并可以调用其他后端。 该应用程序在一台主机上运行。我想在同一主机上运行其他应用程序-Sidecar。这应该: 1)Sidecar应该接收后端应用程序的所有传出流量,以添加其他身份验证标头。 2)Sidecar应该会收到所有传入流量,并检查auth标头,然后转发到Backend应用。

Sidecar应该是spring-boot应用程序。我将选择-Spring云网关。

解决1)我将通过代理调用所有后端应用程序请求-代理是Sidecar cloud GW 解决2)我将只公开接收所有传入流量并转发到后端应用的Sidecar端点/ **

当后端应用程序启动传出请求并将Sidecar用作代理时,下一个代码有效

@SpringBootApplication
@RestController
public class DemoApplication {

public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class,args);
}

@RequestMapping("/**")
public Mono<ResponseEntity<byte[]>> proxy(ProxyExchange<byte[]> proxy,ServerHttpRequest serverHttpRequest) throws Exception {
    return proxy.uri(serverHttpRequest.getURI().toString()).forward();
}
}

同时,以上代码拦截所有外部传入请求,并将其再次发送到Sidecar,从而导致无限循环。

有人建议写Sidecar(Spring云网关),一方面将它用作传出请求的代理,另一方面将所有传入请求转发到后端应用程序

谢谢

解决方法

我已经使用Spring Cloud构建微服务架构;通过spring boot构建的大多数微服务都在eureka中注册。但是有些遗留服务无法轻松迁移到Spring Boot。因此,我们决定使用Sidecar来支持旧版服务;

使用Spring Cloud Gateway作为Sidecar

Spring Cloud提供了 spring cloud辅助工具,但它是使用zuul构建的。我已将zuul替换为spring cloud gateway

spring cloud sidecar将在eureka中注册旧服务元信息,而不是在sidecar本身中注册。因此,对旧版服务的请求将不会被sidecar过滤,因此我们无法管理来自微服务的请求。我修改了spring cloud sidecar以解决此问题,sidecar应用程序元信息将在eureka中注册。因此,其他eureka客户端将从eureka服务器而不是旧版服务中找到sidecar application

转发进出请求

如何确定来自旧服务或微服务的请求来自何处?请求中有一个来自其他微服务的 自定义标头 ,该标头在旧请求中将不存在。因此,我们使用标头来确定请求是传入还是传出。

我定义了一个SidecarHeaderRoutePredicateFactory,它扩展了AbstractRoutePredicateFactory,以构建一个gateway predicate,它将检查请求头。

如果请求包含 自定义标头 ,这意味着该请求来自其他微服务而不是旧服务,则该请求将转发到旧服务;

否则,请求来自旧服务,将不执行任何操作。 Spring cloud gateway将通过eureka发现客户端将请求转发到微服务。

来自旧服务的任何请求都具有不同的url模型;网址模板为 http://{sidecarhost:sidecarport}/{invoke-service-name}/api/xxx。 Spring云网关将通过请求前缀invoke-service-name确定应将请求转发到何处。

public class SidecarHeaderRoutePredicateFactory
        extends AbstractRoutePredicateFactory<SidecarHeaderRoutePredicateFactory.Config> {


    public SidecarHeaderRoutePredicateFactory() {
        super(SidecarHeaderRoutePredicateFactory.Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config c) {
        return exchange -> {
            final String value = exchange.getRequest().getHeaders().getFirst(c.header);
            return StringUtils.equals(value,c.value);
        };
    }
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Config {
        private String header;
        private String value;
    }
}
@Bean
public RouteLocator sidecarRouteLocator(final RouteLocatorBuilder builder) {
    final String uri = "http://xxx";
    final SidecarHeaderRoutePredicateFactory.Config config =
            new SidecarHeaderRoutePredicateFactory.Config(
                    sidecarProperties.getHeaderName(),sidecarProperties.getHeaderValue());
    return builder.routes()
            .route(
                    p -> p.asyncPredicate(
                            // SidecarHeaderRoutePredicateFactory
                            sidecarHeaderRoutePredicateFactory()
                                .applyAsync(config)
                    ).uri(uri)
            ).build();
}

将自定义标头注入请求

使用GlobalFilter,我们可以拦截网关中的所有请求,我在此处将自定义标头注入请求中。

public class HeaderInjectGlobalFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) {
        ServerWebExchange serverWebExchange = exchange;
        if (the request is from legacy service) {
            final ServerHttpRequest request = exchange.getRequest();
            final ServerHttpRequest newRequest = request.mutate()
                    .header("headerA","valueA")
                    .build();
            serverWebExchange = exchange.mutate().request(newRequest).build();
        }
        return chain.filter(serverWebExchange);
    }
}

如何在请求中检查auth标头

使用WebFilter,我们可以检查标题是否在请求中。

public class AuthWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange,WebFilterChain chain) {
        final ServerHttpRequest request = exchange.getRequest();
        final String value = request.getHeaders().getFirst("auth-header-name");
        if (value is exist || auth header is illegal) {
            throw new RuntimeException("error");
        }
        return chain.filter(exchange);
    }
}

摘要

我已经用尤里卡和Spring Cloud Gateway构建了Sidecar。这就是我制造小车的过程。我没有尝试过在场景中没有尤里卡的情况下建造边车,但也许定义自定义网关转发规则会起作用。

我希望这会对您有所帮助。谢谢。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?