【SpringCloud】Gateway

发布时间 2023-06-30 15:37:40作者: lihewei

1. Gateway简介

​ 网关是微服务最边缘的服务,直接暴露给用户,用来做用户和微服务的桥梁。Gateway网关是我们服务的守门神,所有微服务的统一入口。Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关。Gateway 本身也是一个微服务,需要注册到Eureka或其他注册中心。

  • 没有网关:客户端直接访问我们的微服务,会需要在客户端配置很多的 ip:port,如果服务并发比较大,则无法完成负载均衡

  • 有网关:客户端访问网关,网关来访问微服务,(网关可以和注册中心整合,通过服务名称找到目标的 ip:prot)这样只需要使用服务名称即可访问微服务,可以实现负载均衡,可以实现 token 拦截,权限验证,限流等操作

1.1 Gateway工作流程

客户端向 springcloud Gateway 发出请求,然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器来将请求发送到我们实际的服务的业务逻辑,然后返回。 过滤器可能会在发送请求之前【pre】或之后【post】执行业务逻辑,对其进行加强或处理。Filter 在

  • 【pre】 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等

  • 【post】 类型的过滤器中可以做响应内容、响应头的修改、日志的输出,流量监控等

总结:Gateway 的核心逻辑也就是 路由转发 + 执行过滤器链

1.2 Gateway三大核心概念

?Route(路由)

重点 和 eureka 结合做动态路由

路由信息的组成:由一个 ID、一个目的 URL、一组断言工厂、一组 Filter 组成。如果路由断言为真,说明请求 URL 和配置路由匹配。

?Predicate(断言)

就是一个返回 bool 的表达式

?Filter(过滤)

Spring Cloud Gateway 中的 Filter 分为两种类型的 Filter,分别是 Gateway Filter 和Global Filter。过滤器 Filter 将会对请求和响应进行修改处理。

一个是针对某一个路由(路径)的 filter 对某一个接口做限流

一个是针对全局的 filter token ip 黑名单

1.3 Gateway和Nginx的区别

​ Nginx 在做路由,负载均衡,限流之前,都有修改 nginx.conf 的配置文件,把需要负载均衡,路由,限流的规则加在里面。Eg:使用 nginx 做 tomcat 的负载均衡,但是 gateway 不同,gateway 自动的负载均衡和路由,gateway 和 eureka 高度集成,实现自动的路由,和 Ribbon 结合,实现了负载均衡(lb协议),gateway 也能轻易的实现限流和权限验证。

Nginx(c)比 gateway(java)的性能高一点。

本质的区别呢?

Nginx (更大 服务器级别的)

Gateway 项目级别的)

2. Gateway快速入门

实现步骤:

  1. 创建SpringBoot工程gateway_server
  2. 勾选starter:Gateway、Eureka客户端
  3. 编写基础配置:端口,应用名称,注册中心地址
  4. 编写路由规则:唯一表示id,路由url地址,路由限定规则
  5. 启动网关服务进行测试

2.1 新建项目选择依赖

2.2 修改启动类

package com.lihw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient //Eureka客户端
public class GatewayServiceApplication {

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

}

2.3 新建两个服务

新建两个服务对外提供接口供测试使用(省略)

provider-order-service:8081

consumer-user-service:8082

2.4 修改配置文件

server:
  port: 80
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      enabled: true #开启网关,默认是开启的
      routes: #设置路由,注意是数组,可以设置多个,按照 id 做隔离
        - id: provider-order-service #路由 id,没有要求,保持唯一即可
          uri: http://localhost:8081 #设置真正的服务 ip:port
          predicates: #断言匹配
            - Path=/info/** #和服务中的路径匹配,是正则匹配的模式
        - id: consumer-user-service
          uri: http://localhost:8082
          predicates:
            - Path=/info/** #如果匹配到第一个路由,则第二个就不会走了,注意这不是负载均衡
#eureka 的配置
eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

2.5 测试

gateway端口80可不输入

http://localhost/info/provider2

3. Gateway的两种路由配置方式

3.1代码路由方式(了解)

package com.lihw.test;

@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes
                .route("path_rote_guonei", r -> r.path("/guonei").uri("http://news.baidu.com/guonei"))
                .route("path_rote_guoji", r -> r.path("/guoji").uri("http://news.baidu.com/guoji"))
                .route("path_rote_tech", r -> r.path("/tech").uri("http://news.baidu.com/tech"))
                .route("path_rote_lady", r -> r.path("/lady").uri("http://news.baidu.com/lady"))
                .build();
        return routes.build();
    }
}

3.2使用yml方式(重点)

server:
  port: 80
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      enabled: true #开启网关,默认是开启的
      routes: #设置路由,注意是数组,可以设置多个,按照 id 做隔离
        - id: provider-order-service #路由 id,没有要求,保持唯一即可
          uri: http://localhost:8081 #设置真正的服务 ip:port
          predicates: #断言匹配
            - Path=/info/** #和服务中的路径匹配,是正则匹配的模式
        - id: consumer-user-service
          uri: http://localhost:8082
          predicates:
            - Path=/info/** #如果匹配到第一个路由,则第二个就不会走了,注意这不是负载均衡
            
#eureka 的配置
eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

4. Gateway动态路由

​ 从之前的配置里面我们可以看到我们的 URL 都是写死的,这不符合我们微服务的要求,我们微服务是只要知道服务的名字,根据名字去找,而直接写死就没有负载均衡的效果了默认情况下 Gateway 会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能需要注意的是 uri 的协议为 lb(load Balance),表示启用 Gateway 的负载均衡功能。lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri协议:就是双方约定的一个接头暗号

4.1 实现动态路由

  • 修改配置文件实现动态路由:?

    • spring.cloud.gateway.discovery.locator.enabled=true
    • spring.cloud.gateway.discovery.locator.lower-case-service-id=true
    • spring.cloud.gateway.routes[0].id=路由 id,没有要求,保持唯一即可
    • spring.cloud.gateway.routes[0].uri=lb://注册中心中对应的服务名
    • spring.cloud.gateway.routes[0].predicates[0]=Path=/info/** #断言匹配:和服务中的路径匹配,是正则匹配的模式
  • 使用lb协议:配置文件改成 uri: lb://provider-order-service (如果改服务是集群,则根据微服务名称做负均衡)

    server:
      port: 80
    spring:
      application:
        name: gateway-80
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true #开启动态路由
              lower-case-service-id: true #动态路由小驼峰规则
          routes: #设置路由,注意是数组,可以设置多个,按照 id 做隔离
            - id: provider-service #路由 id,没有要求,保持唯一即可
              uri: lb://provider-order-service #使用 lb 协议 微服务名称做负均衡
              predicates: #断言匹配
                - Path=/info/** #和服务中的路径匹配,是正则匹配的模式
    
    #eureka 的配置
    eureka:
      instance:
        instance-id: ${spring.application.name}:${server.port}
        prefer-ip-address: true
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
    
  • 测试:http://localhost/info/provider,provider有两个服务

  • 当我们新起一个服务,那么 gateway 可以实现服务发现功能,我们并没有再 routers 里面配置路由规则,然而我们访问 新起的 provider-order-service,测试访问http://localhost/provider-order-service/info 可以成功,这就是动态路由和服务发现

4.2 添加/删除路由前缀

【目的:隐藏后端服务的真是路径】

添加前缀

在gateway中可以通过配置路由的过滤器PrefixPath 实现映射路径中的前缀添加。可以起到隐藏接口地址的作用,避免接口地址暴露。

  1. 配置请求地址添加路径前缀过滤器

    spring:
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由id,可以随意写
              # 代理服务地址;lb表示从Eureka中获取具体服务
              uri: lb://user-service
              # 路由断言,配置映射路径
              predicates:
                - Path=/**  #拦截所有 拦截路径不要出现包含关系,都要是互斥
              # 请求地址添加路径前缀过滤器
              filters:
                - PrefixPath=/user   #在访问的地址前面自动添加/user
    
  2. 重启GatewayApplication

  3. 配置完成的效果:

配置 访问地址 路由地址
PrefixPath=/user localhost:10010/findById?id=1 localhost:9091/user/findById?id=1
PrefixPath=/user/abc localhost:10010/findById?id=1 localhost:9091/user/abc/findById?id=1

去除前缀

在gateway中通过配置路由过滤器StripPrefix,实现映射路径中地址的去除。通过StripPrefix=1来指定路由要去掉的前缀个数。如:路径/api/user/1将会被路由到/user/1。

  1. 配置去除路径前缀过滤器

    spring:
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由id,可以随意写
              # 代理服务地址;lb表示从Eureka中获取具体服务
              uri: lb://user-service
              # 路由断言,配置映射路径
              predicates:
                - Path=/**
              # 去除路径前缀过滤器 (局部过滤器,仅对当前路由有效)
              filters:
                - StripPrefix=1  #去除前缀的个数
    
  2. 重启GatewayApplication

  3. 访问查看效果

配置 访问地址 路由地址
StripPrefix=1 localhost:10010/api/user/findById?id=1 localhost:9091/user/findById?id=1
StripPrefix=2 localhost:10010/aa/api/user/findById?id=1 localhost:9091/user/findById?id=1

5. Predicate断言工厂

在 gateway 启动时会去加载一些路由断言工厂(判断一句话是否正确 一个 boolean 表达式 )

断言就是路由添加一些条件(丰富路由功能的)通俗的说,断言就是一些布尔表达式,满足条件的返回 true,不满足的返回 false。Spring Cloud Gateway 将路由作为 Spring WebFlux HandlerMapping 基础架构的一部分进行匹配。Spring Cloud Gateway 包括许多内置的路由断言工厂。所有这些断言都与 HTTP请求的不同属性匹配。您可以将多个路由断言可以组合使用Spring Cloud Gateway 创建对象时,使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route

使用方法

使用断言判断时,我们常用 yml 配置文件的方式进行配置

server:
	port: 80
spring:
	application:
		name: gateway-80
	cloud:
		gateway:
			enabled: true #开启网关,默认是开启的
      			routes: #设置路由,注意是数组,可以设置多个,按照 id 做隔离
      			  - id: provider-service #路由 id,没有要求,保持唯一即可
      			    uri: lb://provider-order-service #使用 lb 协议 微服务名称做负均衡
      			    predicates: #断言匹配
      			    	- Path=/info/** #和服务中的路径匹配,是正则匹配的模式
      			    	- After=2020-01-20T17:42:47.789-07:00[Asia/Shanghai] #此断言匹配发生在指定日期时间之后的请求,ZonedDateTime dateTime=ZonedDateTime.now()获得
      			    	- ?
  • Path=/info/** #和服务中的路径匹配,是正则匹配的模式
  • After=2020-01-20T17:42:47.789-07:00[Asia/Shanghai] #此断言匹配发生在指定日期时间之后的请求,ZonedDateTime dateTime=ZonedDateTime.now()获得
  • Before=2020-06-18T21:26:26.711+08:00[Asia/Shanghai] #此断言匹配发生在指定日期时间之前的请求
  • Between=2020-06-18T21:26:26.711+08:00[Asia/Shanghai],2020-06-18T21:32:26.711+08:00[Asia/Shanghai] #此断言匹配发生在指定日期时间之间的请求
  • Cookie=name,xiaobai #Cookie 路由断言工厂接受两个参数,Cookie 名称和 regexp(一个 Java 正则表达式)。此断言匹配具有给定名称且其值与正则表达式匹配的 cookie
  • Header=token,123456 #头路由断言工厂接受两个参数,头名称和 regexp(一个 Java 正则表达式)。此断言与具有给定名称的头匹配,该头的值与正则表达式匹配。
  • Host=**.bai.com: #主机路由断言工厂接受一个参数:主机名模式列表。该模式是一个 ant 样式的模式。作为分隔符。此断言匹配与模式匹配的主机头
  • Method=GET,POST #方法路由断言工厂接受一个方法参数,该参数是一个或多个参数:要匹配的 HTTP 方法
  • Query=username,cxs #查询路由断言工厂接受两个参数:一个必需的 param 和一个可选的 regexp(一个 Java 正则表达式)。
  • RemoteAddr=192.168.1.1/24 #RemoteAddr 路由断言工厂接受一个源列表(最小大小 1),这些源是 cidr 符号(IPv4 或 IPv6)字符串,比如 192.168.1.1/24(其中 192.168.1.1 是 IP 地址,24 是子网掩码)。

配置权重

80%的请求,由 https://weighthigh.org 这个 url 去处理

20%的请求由 https://weightlow.org 去处理

spring:
	cloud:
		gateway:
			- id: weight_high
			 uri: https://weighthigh.org
			 predicates:
				- Weight=group1, 8
			- id: weight_low
			 uri: https://weightlow.org
			 predicates:
				- Weight=group1, 2

总结:Predicate 就是为了实现一组匹配规则,让请求过来找到对应的 Route 进行处理

6. Gateway自定义过滤器

gateway 里面的过滤器和 Servlet 里面的过滤器,功能差不多,路由过滤器可以用于修改进入Http 请求和返回 Http 响应

按生命周期分两种:

  • pre 在业务逻辑之前
  • post 在业务逻辑之后

按种类分也是两种:

  • GatewayFilter:需要配置某个路由,才能过滤。如果需要使用全局路由,需要配置 Default Filters。
  • GlobalFilter:全局过滤器,不需要配置路由,系统初始化作用到所有路由上

全局过滤器 统计请求次数 限流 token 的校验 ip 黑名单拦截 跨域本质(filter)144 开头的电话 限制一些 ip 的访问

6.1 校验请求中是否携带token

  1. 创建配置类GlobalFilterConfigs

    package com.lihw.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    @Component
    @Slf4j
    public class GlobalFilterConfig implements GlobalFilter, Ordered {
    
       @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("进入了我自己的全局过滤器");
            String token = exchange.getRequest().getQueryParams().getFirst("token");
            if (token == null) {
                log.error("token 为空,说明没有认证");
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                Map<String, Object> map = new HashMap<>();
                map.put("code", HttpStatus.UNAUTHORIZED);
                map.put("msg", "token为空,拒绝访问");
                response.getHeaders().add("content-Type",
                        "application/json;charset=UTF-8");
                ObjectMapper objectMapper = new ObjectMapper();
                byte[] bytes = new byte[0];
                try {
                    bytes = objectMapper.writeValueAsBytes(map);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                DataBuffer buffer = response.bufferFactory().wrap(bytes);
                return response.writeWith(Mono.just(buffer));
    
    //            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
    //            return exchange.getResponse().setComplete();
            }
            log.info("验证通过");
            return chain.filter(exchange);
        }
    
        /**
         * order 越小 越先执行
         * @return
         */
        @Override
        public int getOrder() {
            return 0;
        }
    
    }
    
    
    1. 测试:

    携带token的请求

    未携带token的请求

6.2 IP 认证拦截实战

  1. 创建拦截器配置类IPCheckFilter

    package com.lihw.config;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    @Slf4j
    public class IPCheckFilter implements GlobalFilter, Ordered {
        @SneakyThrows
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String ip = exchange.getRequest().getHeaders().getHost().getHostName();
            //这里写死了,只做演示
            if (ip.equals("localhost")) {
                //说明是黑名单里面的 ip
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                Map<String, Object> map = new HashMap<>();
                map.put("code", HttpStatus.UNAUTHORIZED);
                map.put("msg", "非法访问");
                response.getHeaders().add("content-Type",
                        "application/json;charset=UTF-8");
                ObjectMapper objectMapper = new ObjectMapper();
                byte[] bytes = objectMapper.writeValueAsBytes(map);
                DataBuffer buffer = response.bufferFactory().wrap(bytes);
                return response.writeWith(Mono.just(buffer));
            }
            return chain.filter(exchange);
        }
    
        /**
         * 设置此过滤器的执行顺序
         *
         * @return
         */
        @Override
        public int getOrder() {
            return 1;
        }
    }
    
    
  2. 测试

6.3 编写自定义过滤工厂

参考:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#writing-custom-gatewayfilter-factories

To write a GatewayFilter, you must implement GatewayFilterFactory. You can extend an abstract class called AbstractGatewayFilterFactory. The following examples show how to do so:

  • PreGatewayFilterFactory.java

    public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
    
        public PreGatewayFilterFactory() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            // grab configuration from Config object
            return (exchange, chain) -> {
                //If you want to build a "pre" filter you need to manipulate the
                //request before calling chain.filter
                ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
                //use builder to manipulate the request
                return chain.filter(exchange.mutate().request(builder.build()).build());
            };
        }
    
        public static class Config {
            //Put the configuration properties for your filter here
        }
    
    }
    
  • PostGatewayFilterFactory.java

    public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {
    
        public PostGatewayFilterFactory() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            // grab configuration from Config object
            return (exchange, chain) -> {
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    ServerHttpResponse response = exchange.getResponse();
                    //Manipulate the response in some way
                }));
            };
        }
    
        public static class Config {
            //Put the configuration properties for your filter here
        }
    
    }
    

7. Gateway限流

通俗的说,限流就是限制一段时间内,用户访问资源的次数,减轻服务器压力,限流大致分为两种:

  1. IP 限流(5s 内同一个 ip 访问超过 3 次,则限制不让访问,过一段时间才可继续访问)
  2. 请求量限流(只要在一段时间内(窗口期),请求次数达到阀值,就直接拒绝后面来的访问了,过一段时间才可以继续访问)(粒度可以细化到一个 api(url),一个服务)

7.1 令牌桶算法限流

入不敷出

  1. 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
  2. 根据限流大小,设置按照一定的速率往桶里添加令牌;
  3. 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
  4. 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
  5. 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;

7.2 Gateway 结合 redis 实现请求量限流

Spring Cloud Gateway 已经内置了一个 RequestRateLimiterGatewayFilterFactory,可以直接使用。目前 RequestRateLimiterGatewayFilterFactory 的实现依赖于 Redis,所以我们还要引入spring-boot-starter-data-redis-reactive。

7.2.1 添加依赖

<!--限流要引入 Redis-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

7.2.2 修改配置文件

server:
    port: 80 # 网关一般是80
spring:
    application:
        name: gateway-server
    cloud:
        gateway:
            enabled: true # =只要加了依赖 默认开启
            routes: # 如果一个服务里面有100个路径  如果我想做负载均衡 ??   动态路由
                -   id: login-service-route  # 这个是路由的id 保持唯一即可
                    #                    uri: http://localhost:8081   # uri统一资源定位符 
                    uri: lb://login-service   # uri统一资源定位符
                    predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
                        - Path=/doLogin  # 匹配规则  只要你Path匹配上了/doLogin 就往 uri 转发 并且将路径带上
                        - After=2022-03-22T08:42:59.521+08:00[Asia/Shanghai]
                        - Method=GET,POST
                    filters:
                        -   name: RequestRateLimiter  # 这个是过滤器的名称
                            args: # 这个过滤器的参数
                                key-resolver: '#{@apiKeyResolver}' # 通过spel表达式取 ioc容器中bean的值
                                redis-rate-limiter.replenishRate: 1  # 生成令牌的速度
                                redis-rate-limiter.burstCapacity: 3  # 桶容量
            discovery:
                locator:
                    enabled: true  # 开启动态路由  开启通用应用名称 找到服务的功能
                    lower-case-service-id: true  # 开启服务名称小写
                    
    redis: #redis 的配置
		host: 10.1.61.121
		port: 6379
		database: 0
eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/
        registry-fetch-interval-seconds: 3 # 网关拉去服务列表的时间缩短
    instance:
        hostname: localhost
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}

在上面的配置文件,配置了 redis 的信息,并配置了 RequestRateLimiter 的限流过滤器,

该过滤器需要配置三个参数:

  • burstCapacity:令牌桶总容量。
  • replenishRate:令牌桶每秒填充平均速率。
  • key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据‘#{@beanName}’从 Spring 容器中获取 Bean 对象。

7.2.3 创建配置类

package com.lihw.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

@Configuration
public class RequestLimiterConfig {

    /**
     * 针对某一个接口 ip来限流  /doLogin    每一个ip 10s只能访问3次
     */
    @Bean
    @Primary // 主候选的
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
    }

    /**
     * 针对这个路径来限制  /doLogin
     * api 就是 接口  外面一般把gateway    api网关  新一代网关
     */
    @Bean
    public KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }
}

8. 跨域配置

ajax 同源策略

因为网关是微服务的边缘 所有的请求都要走网关 跨域的配置只需要写在网关即可

@Configuration
public class CorsConfig {
	@Bean
	public CorsWebFilter corsFilter() {
		CorsConfiguration config = new CorsConfiguration();
		config.addAllowedMethod("*");
		config.addAllowedOrigin("*");
		config.addAllowedHeader("*");
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
		source.registerCorsConfiguration("/**", config);
		return new CorsWebFilter(source);
	}
}

yaml

spring:
	cloud:
		gateway:
			globalcors:
				corsConfigurations:
				'[/**]': # 针对哪些路径
					allowCredentials: true // 这个是可以携带 cookie