Spring Cloud Gateway----shock wave 2

167 阅读4分钟

功能:

Spring Cloud网关的目的是提供一种简单而有效的方法来路由到API,并向它们提供跨领域的关注,例如:安全性,监视/度量和弹性。

对比:

微信截图_20210630145426.png 官方对比

微信截图_20210630145531.png

工作原理:

客户端向Spring Cloud网关发出请求。
如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。
该处理程序运行通过特定于请求的筛选器链发送请求。
筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前或之后执行逻辑。执行所有“前置”过滤器逻辑,然后发出代理请求。
发出代理请求后,将执行“后”过滤器逻辑。

微信截图_20210630150050.png

引入依赖:

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

配置文件

server:
  port: 8711
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
#      discovery:
#        locator:
#          # 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
#          enabled: true
        # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
        routes:
          # 路由标识(id:标识,具有唯一性)   转发指定地址并传入参数
          - id: CLIENT1-SERVICE
            # 目标服务地址(uri:地址,请求转发后的地址)
            uri: http://localhost:8702
            # 路由条件(predicates:断言,匹配 HTTP 请求内容)
            predicates:
              ## 匹配路径
              - Path=/client1/get/**
#            filters:
#              - StripPrefix=1
            # 过滤器(filters:过滤器,过滤规则)
eureka:
  instance:
    preferIpAddress: true
    instance-id: gateway_service #注册中心右边的位置的显示
  client:
    serviceUrl:
      defaultZone: http://localhost:8701/eureka

ps:其他断言可以参考官方文档www.springcloud.cc/spring-clou…

路由测试:

分别启动注册中心,client1和gateway

微信截图_20210630151509.png

自定义过滤器

package org.example.filter;

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.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("进入自定义过滤器");
        Object object;
        String key = exchange.getRequest().getQueryParams().getFirst("id");
        System.out.println("传入参数为:"+key);
        if(!"123".equals(key)){
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

测试:

微信截图_20210630154113.png

自定义路由断言工厂

package org.example.predicateFactory;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * @author shock wave 2
 * @version 1.0
 * @description: TODO 自定义断言工厂需要继承AbstractRoutePredicateFactory重写apply方法
 * @date 2021/6/30 15:48
 */
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

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

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String userName = serverWebExchange.getRequest().getQueryParams().getFirst("userName");
                if(StringUtils.isBlank(userName)){
                    return false;
                }
                //检查请求参数中的userName是否与配置的数据相同,如果相同则允许访问,否则不允许访问
                if(userName.equals(config.getName())){
                    return true;
                }
                return false;
            }
        };
    }
    /**
     * @description: TODO 获取配置参数
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name");
    }
    @Data
    public static class Config {
        private String name;
    }
}

yml文件修改

server:
  port: 8711
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
#      discovery:
#        locator:
#          # 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
#          enabled: true
        # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
        routes:
          # 路由标识(id:标识,具有唯一性)   转发指定地址并传入参数
          - id: CLIENT1-SERVICE
            # 目标服务地址(uri:地址,请求转发后的地址)
            uri: http://localhost:8702
            # 路由条件(predicates:断言,匹配 HTTP 请求内容)
            predicates:
              ## 匹配路径
              - Path=/client1/**
              #可以使用这两种方式
#              - name: My
              #前面为自定义路由断言工厂的前缀(注意:这个地方名字一定要命名正确。。。RoutePredicateFactory)
              - My=shockwave
#            filters:
#              - StripPrefix=1
            # 过滤器(filters:过滤器,过滤规则)
eureka:
  instance:
    preferIpAddress: true
    instance-id: gateway_service #注册中心右边的位置的显示
  client:
    serviceUrl:
      defaultZone: http://localhost:8701/eureka

测试

先随便来个参数

微信截图_20210630170747.png 使用正常参数

图片.png

自定义局部过滤器工厂

代码

package org.example.filter;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @author shock wave 2
 * @version 1.0
 * @description: TODO
 * @date 2021/6/30 17:10
 */
@Component
@Slf4j
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
    //构造器是必须要的,去掉这个会报错,一般idea会提示加上这个,但是我的idea自定义过滤工厂的时候并没有红线提醒,需手动加上
    public MyGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String version = exchange.getRequest().getQueryParams().getFirst("version");
            if (!config.getVersion().equals(version)) {
                log.info("参数错误");
            }
            return chain.filter(exchange);
        };


    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("version");
    }
    @Data
    public static class Config {
        private String version;
    }
}

yml配置

server:
  port: 8711
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
#      discovery:
#        locator:
#          # 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
#          enabled: true
        # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
        routes:
          # 路由标识(id:标识,具有唯一性)   转发指定地址并传入参数
          - id: CLIENT1-SERVICE
            # 目标服务地址(uri:地址,请求转发后的地址)
            uri: http://localhost:8702
            # 路由条件(predicates:断言,匹配 HTTP 请求内容)
            predicates:
              ## 匹配路径
              - Path=/client1/**
              #可以使用这两种方式
#              - name: My
              #前面为自定义路由断言工厂的前缀(注意:这个地方名字一定要命名正确。。。RoutePredicateFactory)
              - My=shockwave
            filters:
              - My=sr
            # 过滤器(filters:过滤器,过滤规则)
eureka:
  instance:
    preferIpAddress: true
    instance-id: gateway_service #注册中心右边的位置的显示
  client:
    serviceUrl:
      defaultZone: http://localhost:8701/eureka

测试

微信截图_20210630173819.png 结果

微信截图_20210630173828.png