Реализация шлюза микросервисов на основе SpringCloudGateway

задняя часть Микросервисы Spring Cloud

Я слышал, что поиск в WeChat для «Java Fish Aberdeen» станет сильнее!

Эта статья включена вgithubа такжеgitee, который содержит мою полную серию статей по Java, вы можете прочитать его для изучения или интервью.

(1) Что такое шлюз микросервисов

После того, как серверная часть написала все микросервисы, она, наконец, передается интерфейсу для вызова. Все мы знаем, что у каждого микросервиса есть свой номер порта, и если внешний интерфейс напрямую вызывает микросервис по IP плюс порт, это будет очень хлопотно. Также может быть очень сложно увеличить лимит запросов. В это время появляется шлюз микросервиса.

Шлюз микросервиса снова становится шлюзом API, который является единственным внешним входом в систему. Шлюз AP1 инкапсулирует внутреннюю архитектуру системы и предоставляет индивидуальный AP1 для каждого клиента. Суть подхода шлюза API заключается в том, что все клиенты и потребители получают доступ к микросервисам через единый шлюз и обрабатывают все некоммерческие функции на уровне шлюза. Обычно шлюз также предоставляет API доступа REST/HTTP. Сервер регистрирует службы и управляет ими через API-GW.

Звучит немного абстрактно, поэтому давайте сделаем картинку, чтобы показать вам: интерфейсные запросы управляются шлюзом микросервиса, а шлюз микросервиса может вызывать каждый микросервис, и в то же время он имеет множество функций.

В обязанности API-шлюза входит аутентификация, мониторинг, балансировка нагрузки, кэширование, фрагментация запросов и управление ими, статическая обработка ответов, а самая важная функция — связь с внешним миром.

Общие реализации шлюза API включают Zuul, traefix, Spring Cloud Gateway и другие. Текущим основным шлюзом микросервисов является Spring Cloud Gateway.

(2) Шлюз Spring Cloud Gateway

Spring Cloud Gateway — это официальный шлюз, разработанный Spring на основе таких технологий, как Spring 5.0, Spring Boot 2.0 и Project Reactor.Он призван предоставить простой и эффективный метод управления унифицированной маршрутизацией API и унифицированный интерфейс доступа для микросервисной архитектуры.. В качестве шлюза в экосистеме Spring Cloud Spring Cloud Gateway призван заменить Netflix ZUUL, который не только обеспечивает унифицированный метод маршрутизации, но также предоставляет базовые функции шлюза на основе цепочки фильтров, такие как: безопасность, мониторинг/покупка, ограничение потока и т.д.

2.1 Основные понятия:

маршрут: Маршрутизация является самой основной частью шлюза.Информация о маршрутизации состоит из идентификатора, URL-адреса назначения, набора фабрик утверждений и набора фильтров.

предикаты: функция утверждения в java8, функция утверждения в Spring Cloud Gateway позволяет разработчикам определять и сопоставлять любую информацию из запроса Http. Когда утверждение истинно, маршрут сопоставляется.

фильтр: фильтровать запросы и ответы

Давайте на практике управлять шлюзом микросервиса.Этот проект зависит от Nacos и Sentinel.Если вам не нужна эта часть, вы можете удалить ее в файле конфигурации.

2.2 Настройка окружения:

1. Внедрить зависимости импорта проекта

Создайте проект api_gateway_server и импортируйте зависимости в pom.xml в проект.Глобальные зависимости проекта следующие:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.javayz</groupId>
    <artifactId>nacos-discovery-spring-cloud-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-discovery-spring-cloud-gateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <projec.build.sourceEncoding>UTF-8</projec.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!--springcloudgateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>

    <!--当前项目继承的依赖-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2. Настройте класс запуска

Создайте класс GatewayApplication.

@EnableDiscoveryClient
@SpringBootApplication
public class NacosDiscoverySpringCloudGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosDiscoverySpringCloudGatewayApplication.class, args);
    }
}

3. Напишите файл конфигурации

Файл конфигурации записывается здесь с использованием файла конфигурации формата .yml, а applicaiion.yml создается в папке ресурсов. На что здесь нужно обратить внимание, так это на настройку маршрутов под шлюзом.Сначала используйте id для настройки шлюза, затем определите uri, что означает, что при доступе к порту 9090 (то есть при доступе к шлюзу ), IP-адрес и номер порта автоматически меняются на uri ; Наконец, напишите предикаты утверждения, здесьPath=/index/** означает автоматическое совпадение /index/**маршрут из. Когда адрес запросаhttp://localhost:9090/index/1, шлюз микросервисов получит доступhttp://nacos-discovery-consumer/index/1, это функция шлюза микросервиса для вызова микросервиса.

server:
  port: 9090
spring:
  application:
    name: nacos-discovery-spring-cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #启用discoveryClient网关集成,实现服务发现
      routes:
        - id: route1
          uri: lb://nacos-discovery-consumer  #lb://表示负载均衡
          predicates:  #判断条件
            - Path=/test,/index/**

(3) Подробная конфигурация маршрутизации

В утверждениях маршрутизации много правил сопоставления, раньше был введен только Path, на самом деле правил сопоставления много.

predicates:
    #匹配一个时间,在这个时间之后发生的请求才能访问(格式是ZonedDateTime)
    - After=2021-03-14T11:31:08.377+08:00[Asia/Shanghai] 
    #匹配一个时间,在这个时间之前发生的请求才能访问(ZonedDateTime)
    - Before=2021-03-14T11:31:08.377+08:00[Asia/Shanghai]
    #匹配两个时间,在这个时间之间才能访问
    - Between=2021-03-14T11:31:08.377+08:00[Asia/Shanghai],2021-03-14T12:31:08.377+08:00[Asia/Shanghai] #路由断言之间
    #路由断言Cookie匹配,匹配给定名称(如master)或者正则表达式,如下面配置,请求的cookie中必须有token=master才能访问
    - Cookie=token,master
    #根据请求头信息匹配,使用方式和cookie类似
    - Header=XXX,XXX
    #根据host地址匹配
    - Host=**.somehost.org   
    #根据请求方法匹配
    - Method=GET  
    #根据请求路径进行匹配 
    - Path=***   
    #根据参数匹配,如果配置为token,则所有的请求必须要带上token=XXX
   - Query=token
   #根据远程IP匹配
   - RemoteAddr=192.168.1.1/24    
   #权重,第一个参数是组名,第二个参数是权重,两个route下配置不同权重,会根据权重负载访问
   - Weight=group1,8

При определении пути запроса мы также можем получить путь запроса в соответствии с именем микросервиса.Ниже приведены два разных способа определения uri для достижения одной и той же функции.

uri: http://127.0.0.1:9001  #当访问9090端口时,自动把ip和端口替换为此uri
uri: lb://userservice #根据微服务名称拉取请求路径

Общая конфигурация:

server:
  port: 9090
spring:
  application:
    name: nacos-discovery-spring-cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #启用discoveryClient网关集成,实现服务发现
      routes:
        - id: route1
          uri: lb://nacos-discovery-consumer  #lb://表示负载均衡
          predicates:  #判断条件
            - Path=/test,/index/**,/**

    nacos:
      discovery:
        server-addr: 192.168.78.128:8848
      username: nacos
      password: nacos

    sentinel:
      eager: true
      transport:
        dashboard: localhost:8080

3.1 Конфигурация пользовательского утверждения

Реализация пользовательского утверждения делится на два этапа. Предположим, мы хотим реализовать собственный фильтр сейчас. Правило состоит в том, что токен должен быть перенесен, и токен должен быть равен указанному значению.

1. Определите класс конфигурации: он содержит значение объекта, которое будет передано в файле конфигурации.

@Data
public class TokenConfig {
    private String token;
}

2. Определите фабрику утверждения маршрута: во-первых, имя должно быть XXXRoutePredicateFactory, унаследованным от AbstractRoutePredicateFactory.

@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> {
    public TokenRoutePredicateFactory(){
        super(TokenConfig.class);
    }
    //用来把配置中的值变成一个集合
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("token");
    }
    //通过jdk1.8的断言接口来返回true或者false,如果是true表示过滤器不拦截
    @Override
    public Predicate<ServerWebExchange> apply(TokenConfig config) {
        return exchange ->{
            //获取request请求参数
            MultiValueMap<String,String> valueMap=exchange.getRequest().getQueryParams();
            boolean flag=false;
            List<String> list=new ArrayList<>();
            //将request请求的value保存到集合里
            valueMap.forEach((k,v)->{
                list.addAll(v);
            });
            //判断有没有和配置文件中相同的值
            for (String s:list){
                log.info("Token"+s);
                if (StringUtils.equalsIgnoreCase(s,config.getToken())){
                    flag=true;
                    break;
                }
            }
            return flag;
        };
    }
}

Наконец, напишите наш пользовательский контент в утверждении:

predicates:  #判断条件
  - Path=/test,/index/**,/**
  - Token=javayz

Только ссылка естьhttp://localhost:9090/sentinel/test1?token=javayzприноситьtoken=javayzзапрос на доступ.

3.2 Утверждение не соответствует настройке страницы 404

Страница 404, предоставленная SpringCloudGateway по умолчанию,

Такая страница действительно недружелюбна, мы можем заставить ее возвращать строку JSON нестандартным способом.

Создайте новый класс с именем MyErrorWebExceptionHandler:

public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    /**
     * 指定响应处理方法为JSON处理
     * @param errorAttributes
     * @return
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(),this::renderErrorResponse);
    }


    /**
     * 根据code获取对应的HttpStatus
     * @param errorAttributes
     * @return
     */
    @Override
    protected int getHttpStatus(Map<String, Object> errorAttributes) {
        return (int) errorAttributes.get("status");
    }

    /**
     * 构建异常信息
     * @param request
     * @param ex
     * @return
     */
    private String buildMessage(ServerRequest request,Throwable ex){
        StringBuilder builder = new StringBuilder("Failed to handle request:");
        builder.append(request.methodName());
        builder.append(" ");
        builder.append(request.uri());
        if (ex!=null){
            builder.append(ex.getMessage());
        }
        return builder.toString();
    }

    /**
     * 返回的json数据
     * @param status
     * @param errorMsg
     * @param data
     * @return
     */
    public static Map<String,Object> response(int status,String errorMsg,Object data){
        Map<String,Object> map=new HashMap<>();
        map.put("code",status);
        map.put("message",errorMsg);
        map.put("data",data);
        return map;
    }
}

Напишите еще один класс конфигурации для внедрения пользовательского кода страницы исключений в контейнер Bean:

@Configuration
public class GatewayConfiguration {
    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties, ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean("myErrorWebExceptionHandler")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes){
        MyErrorWebExceptionHandler exceptionHandler=new MyErrorWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext
        );
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

Наконец, при доступе к 404, он будет возвращен в формате JSON, что удобно для обработки вызывающей стороной.

(4) Фильтр Spring Cloud Gateway

В дополнение к функции маршрутизации запросов Spring Cloud Gateway также поддерживает фильтрацию запросов.

Жизненный цикл:

Фильтры имеют два жизненных цикла: PRE и POST:

PRE: этот фильтр вызывается перед маршрутизацией запроса. Мы можем использовать этот фильтр для реализации аутентификации, выбора запрошенных микросервисов в кластере, регистрации отладочной информации и многого другого.

POST: этот фильтр выполняется после маршрутизации в микросервис. Такие фильтры можно использовать для добавления стандартных заголовков HTTP к ответам, сбора статистики и метрик, отправки ответов от микросервисов клиентам и многого другого.

тип фильтра:

GatewayFilter: применить к одному маршруту или сгруппированному маршруту

GlobalFilter: применяется ко всем маршрутам

Локальные фильтры могут использовать встроенные методы Spring Cloud Gateway, на официальном сайте (docs.spring.IO/spring - уродливо...

Официальный фильтр:

Здесь только несколько:

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

После добавления фильтра AddRequestHeader в заголовок будет добавляться X-Request-red каждый раз, когда запрос перенаправляется в микросервис.

- AddRequestParameter=red, blue

Добавьте параметр запроса, и параметр запроса будет добавляться каждый раз, когда запрос перенаправляется в микрослужбу.

Пользовательский глобальный фильтр:Создайте пакет Filter и создайте новый класс GatewayFilter в пакете.Чтобы настроить глобальный фильтр, вам необходимо наследовать интерфейсы GlobalFilter и Ordered и переписать два метода интерфейса.

@Component
public class GatewayFilter implements GlobalFilter, Ordered {
    //在里面写过滤器的逻辑
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("进入了过滤器");
        //表示继续向下执行
        return chain.filter(exchange);
    }
    //过滤器的优先级,数值越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

Получив доступ, вы можете увидеть, что распечатанная информация появляется на консоли.

Смоделируйте процесс аутентификации пользователя:

Глобальные фильтры могут реализовывать глобальную аутентификацию пользователей. Измените gatewayFilter и напишите логику отказа.Если первый атрибут в полученном запросе запроса не является токеном доступа или пуст, он вернет ошибку HttpStatus.UNAUTHORIZED

@Component
public class GatewayFilter implements GlobalFilter, Ordered {
    //在里面写过滤器的逻辑
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("进入了过滤器");
        String token = exchange.getRequest().getQueryParams().getFirst("access-token");
        if (token==null){
            //如果不存在,认证失败
            System.out.println("没有登陆");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
    //过滤器的优先级,数值越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

Введите в браузере:http://localhost:9090/sentinel/test1, возникает ошибка из-за отсутствия токена доступа

Введите в браузере:http://localhost:9090/sentinel/test1?access-token=1, чтобы получить правильный результат.

(5) Ограничение тока шлюза

В реальной среде такая проблема будет возникать.В то же время будет много запросов на вызов микросервиса.Поступление большого количества запросов может привести к поломке микросервиса,поэтому текущий лимит становится очень важный.

5.1 Некоторые распространенные алгоритмы ограничения тока:

Алгоритм счетчика: Установите значение счетчика в единицу времени.Если запрос на доступ в единицу времени превышает установленное значение, последующие запросы будут отклонены. Он не будет продолжать получать, пока значение в единицу времени не станет ниже этого значения.

алгоритм дырявого ведра: мы можем думать об алгоритме воронки как о воронке. Когда приходит запрос, он сначала попадает в воронку, а затем распределяется по микросервису в соответствии с заданной скоростью вывода. Когда приходит много запросов одновременно, просто подождите в воронке. Чтобы контролировать поток, необходимо установить две переменные: одна — размер ведра, а другая — скорость, с которой течет воронка.

Алгоритм ведра токенов: Алгоритм ведра с маркерами является усовершенствованием алгоритма с дырявым ведром.По сравнению с алгоритмом с дырявым ведром алгоритм ведра с маркерами может допускать определенную степень пакетных вызовов. Принцип алгоритма ведра токенов: в алгоритме ведра токенов есть ведро для хранения фиксированного количества токенов. Токены будут помещаться в ведро с определенной скоростью. После того, как токены в ведре будут заполнены, будет больше не будет жетонов в ведре. положите жетон. Когда приходит запрос, вы должны получить токен в ведре токенов перед вызовом микросервиса.Когда токен в ведре токенов отсутствует, запрос, который приходит позже, должен ждать. В дополнение к ограничению тока алгоритм корзины маркеров также допускает определенную степень пакетных вызовов: например, вместимость корзины маркеров составляет 100, и когда нет запроса, количество маркеров в корзине маркеров заполнено. В это время, если вдруг придет 100 запросов, то эти 100 запросов можно сразу выполнить.

5.2 Интеграция Sentinel для ограничения тока

добавить зависимости

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    <version>1.7.2</version>
</dependency>

Добавьте конфигурацию Sentinel:

spring:
  cloud:
    sentinel:
      eager: true
      transport:
        dashboard: localhost:8080

Вставьте фильтр Sentinel в контейнер для зерен

@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter(){
    return new SentinelGatewayFilter();
}

Таким образом, шлюз может быть ограничен на пути Sentinel.

Для подсказки об ошибке после текущего ограничения мы можем изменить режим подсказки об ошибке по умолчанию, изменить его на пользовательский или перейти на страницу ошибки:

@Configuration
public class GatewayConfiguration {
    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties, ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    //自定义一个错误处理
    @Bean(name = "myBlockRequestHandler")
    public BlockRequestHandler myBlockRequestHandler(){
        BlockRequestHandler blockRequestHandler=new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                return ServerResponse.status(HttpStatus.BAD_GATEWAY)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue("服务被限流了"));
            }
        };
        return blockRequestHandler;
    }

    //用自定义的handler替换掉默认的,或者重定向
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(BlockRequestHandler myBlockRequestHandler){
        //自定义Block处理
        GatewayCallbackManager.setBlockHandler(myBlockRequestHandler);

        //重定向block处理
        //GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("http://www.baidu.com"));
        return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter(){
        return new SentinelGatewayFilter();
    }
}

5.3 Шлюз для междоменного доступа

Это похоже на идею решения кроссдоменности в SpringBoot, настройки CorsConfig

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter(){
        CorsConfiguration configuration=new CorsConfiguration();
        configuration.addAllowedHeader("*");
        configuration.addAllowedMethod("*");
        configuration.addAllowedOrigin("*");

        UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/*",configuration);
        return new CorsWebFilter(source);
    }
}

(6) Резюме

В этой главе много контента, но я объяснил все, с чем в основном столкнется Spring Cloud Gateway, и я надеюсь, что это будет вам полезно.