草庐IT

SpringCloud Alibaba(三) - GateWay网关

xiaoqigui 2023-04-16 原文

1、基本环境搭建

1.1 依赖

<!--  Gatway 网关会和springMvc冲突,不能添加web依赖      -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

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

1.2 配置

# 端口
server:
  port: 9606

# 服务名
spring:
  application:
    name: kgcmall-gatway

  cloud:
    #nacos 配置
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    # 网关配置
    gateway:
      routes: # 路由,是list集合,可以配置多个路由
      	#product模块
        - id: kh96_route_first # 当前route路由的唯一标识,不能重复
          #uri: http://localhost:9602 # 路由转发的目标资源地址,不支持多负载调用,不利于扩展,不推荐
          uri: lb://kgcmall96-prod # lb 从nacos注册中心的服务列表中,根据指定的服务名,调用服务,推荐用法
          predicates: # 指定路由断言配置,支持多个断言,只要断言成功(满足路由转发条件),才会执行转发到目标资源地址访问
            - Path=/prod-gateway/** # 指定path路径断言,必须满足请求地址是/prod-gateway开始,才会执行路由转发
          filters: # 指定路由过滤配置,支持多个过滤器,在断言成功,执行路由转发时,对请求和响应数据进行过滤处理
            - StripPrefix=1 # 在请求断言成功后,执行路由转发时,自动去除第一层的访问路径/prod-gateway
        #user模块
        - id: kh96_route_second
          uri: lb://kgcmall96-user
          predicates:
            - Path=/user-gateway/**
          filters:
            - StripPrefix=1

1.3 测试

1.3.1 nacos

1.3.2 请求测试

1.3.2.1 通过gateway网关调用prod模块

1.3.2.1 通过gateway网关调用user模块

2、路由Route

Route 主要由 路由id、目标uri、断言集合和过滤器集合组成,那我们简单看看这些属性到底有什么作用。

(1)id:路由标识,要求唯一,名称任意(默认值 uuid,一般不用,需要自定义);

(2)uri:请求最终被转发到的目标地址;

(3)order: 路由优先级,数字越小,优先级越高;

(4)predicates:断言数组,即判断条件,如果返回值是boolean,则转发请求到 uri 属性指定的服务中;

(5)filters:过滤器数组,在请求传递过程中,对请求做一些修改;

3、断言 Predicate

Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。

断言就是说: 在什么条件下 才能进行路由转发

3.1 内置路由断言工厂

  • 基于Datetime类型的断言工厂

    • AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期;
      • -After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
    • BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期;
    • BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内;
  • 基于远程地址的断言工厂RemoteAddrRoutePredicateFactory

    • -RemoteAddr=192.168.1.1/24
  • 基于Cookie的断言工厂CookieRoutePredicateFactory(接收两个参数,cookie 名字和一个正则表达式)

    • -Cookie=chocolate, ch
  • 基于Header的断言工厂HeaderRoutePredicateFactory

    • -Header=X-Request-Id, \d+
  • 基于Host的断言工厂HostRoutePredicateFactory

    • -Host=**.testhost.org
  • 基于Method请求方法的断言工厂MethodRoutePredicateFactory

    • -Method=GET
  • 基于Path求路径的断言工厂

    • PathRoutePredicateFactory(接收一个参数,判断请求的URI部分是否满足路径规则)
      • -Path=/foo/
    • `QueryRoutePredicateFactory·
      • -Query=baz, ba.
  • 基于路由权重的断言工厂WeightRoutePredicateFactory(接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发)

    • routes:

      -id: weight_route1 uri: host1 predicates:

      -Path=/product/**

      -Weight=group3, 1

      -id: weight_route2 uri: host2 predicates:

      -Path=/product/**

      -Weight= group3, 9

3.2 自定义路由断言工厂

3.2.1 自定义网关断言工厂 - 权限断言
/**
 * Created On : 28/11/2022.
 * <p>
 * Author : huayu
 * <p>
 * Description: 自定义网关断言工厂-权限断言
 */
@Component //自定义断言工厂,必须是一个组件放入容器才可以生效
public class MyAuthRoutePredicateFactory
        extends AbstractRoutePredicateFactory<MyAuthRoutePredicateFactory.Config> {

    /*
        配置项名MyAuth的配置参数值,映射到断言工厂内部类的属性名
     */
    public static final String MYAUTH_KEY = "myAuth";

    /*
        通过空参构造方法,指定静态内部类,用于接收配置文件中的配置项的内容,即断言(- myAuth=xxx)
     */
    public MyAuthRoutePredicateFactory() {
        super(MyAuthRoutePredicateFactory.Config.class);
    }

    /*
        价格核心配置文件中的自定义配置项的内容,映射到当前配置类的属性中,即Collections.singletonList(DATETIME_KEY);指定的内部属性
     */
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList(MYAUTH_KEY);
    }

    @Override
    public Predicate<ServerWebExchange> apply(MyAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                //自定义权限断言业务逻辑
                if (StringUtils.isNotBlank(config.getMyAuth())) {
                    //判断自定义权限配置参数值,是否和当前指定的值一直,如果一致则断言成功,否则失败
                    return config.getMyAuth().equals("KH96");
                }
                //断言失败
                return false;

            }
        };
    }

    @Data
    public static class Config {
        private String myAuth;
    }

}

可以配置多个参数,案例:https://blog.csdn.net/qq_31155349/article/details/108557969

3.2.2 配置

3.2.2.1 正确配置成功效果
predicates:
	- MyAuth=KH96  # 自定义 权限断言 配置 (注意首字母要大写)

测试效果:

3.2.2.1 正确配置失败效果

predicates:
	- MyAuth=KHxx  # 配置错误信息

测试效果 :

4、过滤器 Filter

1 作用: 过滤器就是在请求的传递过程中,对请求和响应做一些手脚

2 生命周期: Pre Post

3 分类: 局部过滤器(作用在某一个路由上) 全局过滤器(作用全部路由上)

在Gateway中, Filter的生命周期只有两个:“pre” 和 “post”。

PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter。

  1. GatewayFilter:应用到单个路由或者一个分组的路由上。
  2. GlobalFilter:应用到所有的路由上。

4.1 内置的过滤器工厂

过滤器工厂 作用 参数
AddRequestHeader 为原始请求添加Header Header的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加Header Header的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
Hystrix 为路由引入Hystrix的断路器保护 HystrixCommand的名称
FallbackHeaders 为fallbackUri的请求头中添加具体的异常信息 Header的名称
PrefixPath 为原始请求路径添加前缀 前缀路径
PreserveHostHeader 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter 用于对请求限流,限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的URL http状态码及重定向的url
RemoveHopByHopHeadersFilter 为原始请求删除IETF组织规定的一系列Header 默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader 为原始请求删除某个Header Header名称
RemoveResponseHeader 为原始响应删除某个Header Header名称
RewritePath 重写原始的请求路径 原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader 重写原始响应中的某个Header Header名称,值的正则表达式,重写后的值
SaveSession 在转发请求之前,强制执行WebSession::save操作
secureHeaders 为原始响应添加一系列起安全作用的响应头 无,支持修改这些安全响应头的值
SetPath 修改原始的请求路径 修改后的路径
SetResponseHeader 修改原始响应中某个Header的值 Header名称,修改后的值
SetStatus 修改原始响应的状态码 HTTP 状态码,可以是数字,也可以是字符串
StripPrefix 用于截断原始请求的路径 使用数字表示要截断的路径的数量
Retry 针对不同的响应进行重试 retries、statuses、methods、series
RequestSize 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为5M
ModifyRequestBody 在转发请求之前修改原始请求体内容 修改后的请求体内容
ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容
Default 为所有路由添加过滤器 过滤器工厂名称及值

4.2 测试

简单测试几个;

4.2.1 AddRequestHeader

4.2.1.1 添加配置
filters: 
	- AddRequestHeader=X-Request-token,token_kh96  # 添加请求头参数,两个参数,第一个是添加请求头参数名,第二个参数值
4.2.1.2 通过路由调用的方法
@GetMapping("/mallProduct")
public KgcMallProduct mallProduct(@RequestParam Integer pid,
                                  @RequestHeader(value = "X-Request-token", required = false) String gateWayFilterToken) {
    log.info("------ 根据商品编号:{}, 查询商品详情 ------", pid);

    //通过GateWay网关滤器,增加请求头参数
    log.info("------ 通过GateWay网关滤器,增加请求头参数 X-Request-token:{} ------", gateWayFilterToken);
    // 调用业务接口,查询商品详情
    return kgcMallProductService.getMallProductById(pid);
}
4.2.1.3 测试

4.2.2 AddRequestParameter

4.2.2.1 添加配置
filters: 
	- AddRequestParameter=queryName,param_kh96 # 添加普通 请求参数 ,两个参数,第一个是添加请求头参数名,第二个参数值
4.2.2.2 通过路由调用的方法
@GetMapping("/mallProduct")
public KgcMallProduct mallProduct(@RequestParam Integer pid
                                  @RequestParam(value = "queryName", required = false) String gateWayFilterParamQueryName) {
    log.info("------ 根据商品编号:{}, 查询商品详情 ------", pid);
    
    //通过GateWay网关滤器,增加请求参数
    log.info("------ 通过GateWay网关滤器,增加请求头参数 queryName:{} ------", gateWayFilterParamQueryName);

    // 调用业务接口,查询商品详情
    return kgcMallProductService.getMallProductById(pid);
}
4.2.2.3 测试

4.3 自定义网关过滤器

如果带checkParam参数进行参数校验,如果没有携带直接放行;

4.3.1 自定义网关过滤器

/**
 * Created On : 28/11/2022.
 * <p>
 * Author : huayu
 * <p>
 * Description: 自定义网关过滤工厂 -校验过滤器
 */
@Component //自定义网关过滤工厂,必须是一个组件放入容器才可以生效
public class MyCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<MyCheckGatewayFilterFactory.Config> {
    /*
        配置项名MyAuth的配置参数值,映射到断言工厂内部类的属性名
     */
    public static final String MYCHECK_KEY = "myCheck";

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

    public List<String> shortcutFieldOrder() {
        return Arrays.asList(MYCHECK_KEY);
    }

    public GatewayFilter apply(MyCheckGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //自定义网关过滤器实现过滤业务逻辑
                //模拟发送到网关的请求,如果请求携带参数和网关过滤器配置参数一致,放行,如果不一致,直接404
                String checkParam = exchange.getRequest().getQueryParams().getFirst("checkParam");

                //如果携带了checkParam参数  判断获取的请求参数是否和网关过滤器中配置的参数一致
                if (StringUtils.isNotBlank(checkParam)) {
                    //判断是否一致
                    if (checkParam.equals(config.getMyCheck())) {
                        //过滤器放行到目标请求
                        return chain.filter(exchange);
                    }

                    //返回404
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);

                    return exchange.getResponse().setComplete();

                }
                //没有携带参数,直接放行
                return chain.filter(exchange);
            }
        };
    }

    @Data
    public static class Config {
        private String myCheck;
    }

}

4.3.2 带正确的参数

4.3.3 带错误的参数

4.3.4 不带参数

4.4 自定义网关全局过滤(token)

4.4.1 自定义网关全局过滤(token)

携带token放行,不携带,跳转到登录页面(通过跳转到百度模拟)

/**
 * Created On : 28/11/2022.
 * <p>
 * Author : huayu
 * <p>
 * Description: 自定义网关全局过滤,实现token鉴权,实现GlobalFilter 和 Ordered接口
 */
@Component  //必须是spring组件
public class MyTokenGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //自定义全局过滤逻辑,判断请求头中们是否携带了token参数,如果带了就放行,非欧洲就就拒绝
        if (StringUtils.isBlank(exchange.getRequest().getHeaders().getFirst("token"))) {
            //没有携带token,直接重定向到登录页
            //模拟重定向到单点登录网址,临时用百度
            String redirectUrl = "https://www.baidu.com";

            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.SEE_OTHER); //303 错误,重定向
            response.getHeaders().set(HttpHeaders.LOCATION, redirectUrl);

            //结束响应
            return response.setComplete();
        }
        //代了token,
        //TODO token校验
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        //指定全局过滤器的优先级,值越小,优先级越高
        return 0;
    }
}

4.4.2 携带token

4.4.2 不携带token




有关SpringCloud Alibaba(三) - GateWay网关的更多相关文章

  1. ruby-on-rails - 与 ActiveMerchant 一起使用的最佳支付网关是什么? - 2

    我需要使用ActiveMerchant库在我们的一个Rails应用程序中设置支付解决方案。尽管这个问题非常主观,但人们对主要网关(BrainTree、Authorize.net等)的体验如何?它必须:处理定期付款。有能力记入个人帐户。能够取消付款。有办法存储用户的付款详细信息(例如Authotize.netsCIM)。干杯 最佳答案 ActiveMerchant很棒,但在过去一年左右的时间里,我在使用它时发现了一些问题。首先,虽然某些网关可能会得到“支持”——但并非所有功能都包含在内。查看功能矩阵以确保完全支持您选择的网关-http

  2. ruby-on-rails - Ruby 中的存储库或网关模式 - 2

    如何在Ruby中实现存储库或网关模式?我来自C#世界,我通常抽象出我的数据访问,但是使用ActiveRecord作为Ruby中的默认数据访问机制,如何实现这一点并不明显。我通常在C#中做的是使用抽象接口(interface),然后为ECFustomerRepository、NHibernateCustomerRepository和InMemoryCustomerRepository以及依赖具体实现在这种情况下我注入(inject)了匹配的具体实现。那么现在,Ruby方式是什么?!据我所知,在动态语言中你不需要像DI(依赖注入(inject))这样的东西。而且Ruby具有强大的语言特性,

  3. Spring Cloud Gateway 服务网关的部署与使用详细介绍 - 2

    为什么需要服务网关传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。有了网关之后,网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务。使用网关的好处1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;(2)降低函数间的耦合度。一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性(3)解放开发

  4. ruby-on-rails - Puma 和 Nginx 502 Bad Gateway 错误(Ubuntu 服务器 14.04) - 2

    我需要部署我的Rails应用程序,所以我从这里开始执行了所有步骤,https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-puma-and-nginx-on-ubuntu-14-04但是在教程结束时,我得到了这个错误-->“502BadGateway”编辑现在的错误消息-->“很抱歉,出了点问题。”但是Nginx错误输出是相同的,我检查了puma错误消息,但它们只是记录它何时启动以及何时正常停止。位于app_directory/log下的Rails日志不产生任何输出。puma-man

  5. ruby-on-rails - HttpPlatformHandler HTTP 错误 502.3 - 网关错误 - 2

    我正在尝试在IIS上运行Rails,我遵循了提到的步骤here经过斯科特·汉塞尔曼。这里有一些可能有用的信息:Rails版本:5.1.4ruby版本:2.3.3按照设置步骤操作后,我解决了这个问题HTTPError502.3-BadGatewayTherewasaconnectionerrorwhiletryingtoroutetherequest.Mostlikelycauses:TheCGIapplicationdidnotreturnavalidsetofHTTPerrors.Aserveractingasaproxyorgatewaywasunabletoprocessther

  6. ruby-on-rails - activemerchant Paypal 网关配置 - 2

    我正在尝试借助railscasts教程配置我的paypal网关和activemerchant,但我有点困惑,因为网关信息已更改。这是教程中的旧配置:gateway=ActiveMerchant::Billing::PaypalGateway.new(login:"...",password:"...",signature:"...")在我的PaypalSandbox帐户中,我只有这个:端点:“...”客户ID:“...”secret:“……”什么是正确的配置? 最佳答案 您的网关需要的是经典凭据。为了获得这些,您必须首先创建一个Pa

  7. Gateway-路由、过滤器配置 - 2

    相关名称介绍1、Route(路由)路由是网关的基本单元,由ID、URI、一组Predicate、一组Filter组成,根据Predicate进行匹配转发。ID:自定义的路由ID,保持唯一URL:目标服务地址2、Predicate(谓语、断言)路由转发的判断条件,目前SpringCloudGateway支持多种方式,常见如:Path、Query、Method、Header等,写法必须遵循key=vlue的形式3、Filter(过滤器)过滤器是路由转发请求时所经过的过滤逻辑,可用于修改请求、响应内容路由规则路由规则的配置也就是配置Predicate(谓语、断言),下面介绍路由的配置规则:这里以ym

  8. javascript - 使用 Cognito 联合身份进行 API 网关身份验证 - 2

    我想使用CognitoFederatedEntity(允许通过Google等登录),以允许访问Webjavascript应用程序的API网关。我通过使用Google登录设法获得了Cognito的sessionToken,但我仍然停留在用于启用sessiontoken的API网关配置上。整个联合实体身份验证工作流程是否有好的教程?谢谢! 最佳答案 由于您想通过经过身份验证的Cognito身份调用API,因此首先修改身份池的身份验证Angular色以让api执行策略,您可以将托管策略“AmazonAPIGatewayInvokeFull

  9. go - 如何访问代理的 grpc 服务中的请求 header 。 golang 中的 grpc 网关 - 2

    我有一个由grpc-gateway代理的grpc服务器。当我对网关端点进行HTTP调用时,会调用相应的grpc服务方法。现在,grpc服务实现接收到一个包含header的上下文。我不知道如何访问header。当我调试我的grpc服务并设置断点时,这是我的服务接收到的Context对象的结构。现在,如何获取任何HTTP请求header的值? 最佳答案 HTTPheader存储在元数据中。md,ok:=metadata.FromIncomingContext(ctx)应该可以获取传入的元数据。

  10. go - 如何订购为网关及其成员配置 ssh 的软层网络网关 - 2

    如何订购为网关及其成员配置ssh的软层网络网关packagemainimport("fmt""github.com/softlayer/softlayer-go/datatypes""github.com/softlayer/softlayer-go/services""github.com/softlayer/softlayer-go/session""github.com/softlayer/softlayer-go/sl""encoding/json")funcmain(){//SoftLayerAPIusernameandkeyusername:="setme"apikey:=

随机推荐