Spring Cloud : Gateway 网关过滤器 GatewayFilter(四)

目录

一、概述

二、过滤器

二、网关过滤器 GatewayFilter

1. Path 路径过滤器

2. Parameter 参数过滤器

3. Status 状态过滤器

三、全局过滤器 GlobalFilter

1. 自定义过滤器

2. 自定义网关过滤器

3. 统一鉴权


Spring Cloud Gateway 学习专栏

1. Spring Cloud : Gateway 服务网关认识(一)

2. Spring Cloud :整合Gateway 学习 (二)

3. Spring Cloud:Gateway 路由定义定位器 RouteDefinitionLocator (三)

4. Spring Cloud : Gateway 网关过滤器 GatewayFilter(四)

5. Spring Cloud : Gateway 网关限流(五)

6. Spring Cloud:Gateway 集成 Sentinel (六)

7. Spring Cloud : Gateway Redis动态路由 (七)

8. Spring Cloud : Gateway 整合Swagger (八)

 

 

如果发现本文有错误的地方,请大家毫不吝啬,多多指教,欢迎大家评论,谢谢!


一、概述

本篇文章为系列文章,未读第 上一集 的同学请猛戳这里: Spring Cloud :整合Gateway 学习 (二),下面是讲解网关过滤器 GatewayFilter 使用

二、过滤器

Spring Cloud Gateway 根据作用范围划分为 GatewayFilterGlobalFilter,二者区别如下:

  • GatewayFilter:网关过滤器,需要通过 spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过 spring.cloud.default-filters 配置在全局,作用在所有路由上。
  • GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过 GatewayFilterAdapter 包装成 GatewayFilterChain 可识别的过滤器,它为请求业务以及路由的 URI 转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。

  

二、网关过滤器 GatewayFilter

       网关过滤器用于拦截并链式处理 Web 请求,可以实现横切与应用无关的需求,比如:安全、访问超时的设置等。修改传入的 HTTP 请求或传出 HTTP 响应。Spring Cloud Gateway 包含许多内置的网关过滤器工厂一共有 22 个,包括头部过滤器、 路径类过滤器、Hystrix 过滤器和重写请求 URL 的过滤器, 还有参数和状态码等其他类型的过滤器。根据过滤器工厂的用途来划分,可以分为以下几种:Header、Parameter、Path、Body、Status、Session、Redirect、Retry、RateLimiter 和 Hystrix。

 

/resources/articles/spring/spring-cloud/gateway/20181202154250869.png

 

接下来我们举例说明其中一部分如何使用,其余等大家工作中需要应用时再查询资料学习或者咨询我也可以。

1. Path 路径过滤器

  

Path 路径过滤器可以实现 URL 重写,通过重写 URL 可以实现隐藏实际路径提高安全性,易于用户记忆和键入,易于被搜索引擎收录等优点。实现方式如下:

RewritePathGatewayFilterFactory

RewritePath 网关过滤器工厂采用路径正则表达式参数和替换参数,使用 Java 正则表达式来灵活地重写请求路径。

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
            # 断言,路径相匹配的进行路由 (谓词)
            - Path=/user/** # 路径匹配
          filters:
            #将 /user/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - RewritePath=/user/(?<segment>.*),/$\{segment}
  

 访问:http://127.0.0.1:9000/user/user/getUserInfo?userId=1 结果如下:

 

PrefixPathGatewayFilterFactory

PrefixPath 网关过滤器工厂为匹配的 URI 添加指定前缀

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
            # 断言,路径相匹配的进行路由 (谓词)
            - Path=/** # 所有路径都匹配
          filters:
            # 将 /user/getUserInfo?userId=1 重写为 /user/user/getUserInfo?userId=1
            - PrefixPath=/user

 

 访问:http://127.0.0.1:9000/user/getUserInfo?userId=1 结果如下:

 

StripPrefixGatewayFilterFactory

  

StripPrefix 网关过滤器工厂采用一个参数 StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数。

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
            # 断言,路径相匹配的进行路由 (谓词)
            - Path=/** # 所有路径都匹配
          filters:
            # 将 /api/user/getUserInfo?userId=1 重写为 user/getUserInfo?userId=1
            - StripPrefix=1

访问:http://127.0.0.1:9000/api/user/getUserInfo?userId=1 结果如下:

 

 

SetPathGatewayFilterFactory

SetPath 网关过滤器工厂采用路径模板参数。 它提供了一种通过允许模板化路径段来操作请求路径的简单方法,使用了 Spring Framework 中的 uri 模板,允许多个匹配段。

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
             # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api/user/{segment}
          filters:
            # 将 /api/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - SetPath=/user/{segment}

访问:http://127.0.0.1:9000/api/user/getUserInfo?userId=1 结果如下:

 

 

2. Parameter 参数过滤器

AddRequestParameter

网关过滤器工厂会将指定参数添加至匹配到的下游请求中。

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
             # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api/user/{segment}
          filters:
            # 将 /user/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - RewritePath=/user/(?<segment>/?.*), $\{segment}
            # 在下游请求中添加 flag=1
            - AddRequestParameter=flag, 1

修改用户服务的控制层代码

添加一个 getUserParam 获取网关请求自定义参数接口

@GetMapping("getUserParam")
    @ApiOperation(value = "获取用户参数信息")
    public UserResp getUserParam(HttpServletRequest request,
                                @RequestParam("userId") @ApiParam(name = "userId",value = "用户ID") Long userId
    ) {
        String foo = request.getParameter("foo");
        log.info("getUserHead.resp foo={}",foo);
        UserResp userResp = UserResp.builder()
                .id(userId)
                .createTime(new Date())
                .nickname("change")
                .username("Zou.LiPing")
                .roleName("超级管理员")
                .build();
        return userResp;
    }

访问:http://127.0.0.1:9000/user/user/getUserParam?userId=1 控制台结果如下:

UserController     : getUserHead.resp foo=bar

3. Status 状态过滤器

  

SetStatus 网关过滤器工厂采用单个状态参数,它必须是有效的 Spring HttpStatus。它可以是整数 404 或枚举 NOT_FOUND 的字符串表示。

server:
  port: 9000
service-url:
  user-service: http://localhost:8200

spring:
  application:
    name: mall-gateway


  main:
    allow-bean-definition-overriding: true
  ## redis配置
  redis:
    database: 0
    host: 47.103.20.21
    password: zlp123456
    port: 6379
    timeout: 7000

  cloud:
  ## nacos注册中心
    nacos:
      discovery:
        server-addr: 47.103.20.21:8848
    ## gateway配置
    gateway:

      routes:
        - id: user #路由的ID 保证唯一
          uri: ${service-url.user-service} # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
          predicates:
             # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api/user/{segment}
          filters:
            # 将 /user/user/getUserInfo?userId=1 重写为 /user/getUserInfo?userId=1
            - RewritePath=/user/(?<segment>/?.*), $\{segment}
           # 任何情况下,响应的 HTTP 状态都将设置为 404
            - SetStatus=404 			 # 404 或者对应的枚举 NOT_FOUND

访问:http://127.0.0.1:9000/user/user/getUserParam?userId=1 控制台结果如下:

 

三、全局过滤器 GlobalFilter

  

  全局过滤器不需要在配置文件中配置,作用在所有的路由上,最终通过 GatewayFilterAdapter 包装成 GatewayFilterChain 可识别的过滤器,它是请求业务以及路由的 URI 转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。

 

/resources/articles/spring/spring-cloud/gateway/920071-20190929104112590-1124234164.png

1. 自定义过滤器

即使 Spring Cloud Gateway 自带许多实用的 GatewayFilter Factory、Gateway Filter、Global Filter,但是在很多情景下我们仍然希望可以自定义自己的过滤器,实现一些骚操作。

2. 自定义网关过滤器

  自定义网关过滤器需要实现以下两个接口 :GatewayFilterOrdered

创建过滤器

 CustomGatewayFilter.java

@Slf4j
@Component
public class CustomGatewayFilter implements GatewayFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("自定义网关过滤器...");
        return chain.filter(exchange);// 继续向下执行
    }

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

注册过滤器 

package com.example.config;

import com.example.filter.CustomGatewayFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 网关路由配置类
 */
@Configuration
public class GatewayRoutesConfiguration {

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes().route(r -> r
                // 断言(判断条件)
                .path("/product/**")
                // 目标 URI,路由到微服务的地址
                .uri("lb://ai-product")
                // 注册自定义网关过滤器
                .filters(new CustomGatewayFilter())
                // 路由 ID,唯一
                .id("product"))
                .build();
    }

}

访问

注释配置文件中所有网关配置,重启并访问:http://localhost:9000/product/getProduct?productId=1 控制台结果如下:

自定义网关过滤器被执行

3. 统一鉴权

接下来我们在网关过滤器中通过 token 判断用户是否登录,完成一个统一鉴权案例。

AuthorizeFilter

/**
 * 认证过滤器
 * @date: 2021/4/22 19:30
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        // 获取 token 参数
        String token = serverHttpRequest.getHeaders().getFirst("token");

        if (StringUtils.isBlank(token)) {
            serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            return getVoidMono(serverHttpResponse, ResultCode.TOKEN_MISSION);
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {

        return 1;
    }

    /** 
     * 错误信息响应到客户端
     * @param serverHttpResponse Response
     * @param resultCode 错误枚举
     * @date: 2021/4/20 9:13
     * @return: reactor.core.publisher.Mono<java.lang.Void> 
     */
    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, ResultCode resultCode) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(Result.failed(resultCode)).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

}

访问

重启并访问:http://localhost:9000/product/getProduct?productId=1

我在头部请求 token 参数


源码地址

mall-gateway 这个项目

https://gitee.com/gaibianzlp/zlp-mall-demo.git


 

参考链接

1. Spring Cloud 系列之 Gateway 服务网关

 

Spring Cloud Gateway 是基于 Spring WebFlux 框架开发的网关组件,提供了一种简单、轻量级的方式来构建微服务架构中的路由和过滤器。 要实现局部过滤器,可以在 Gateway 配置文件中为特定的路由添加过滤器。以下是一种实现方式: 1. 在 Spring Cloud Gateway 配置文件中,定义一个路由规则并指定路由的 ID 和目标 URL,例如: ```yaml spring: cloud: gateway: routes: - id: my_route uri: http://example.com filters: - name: my_filter args: key: value ``` 2. 创建一个自定义的过滤器类,实现 `GatewayFilter` 接口,例如: ```java import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.http.HttpStatus; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; public class MyFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 这里可以编写自定义的过滤逻辑 // 例如,根据请求头信息进行验证 String authToken = exchange.getRequest().getHeaders().getFirst("Authorization"); if (authToken == null || !authToken.startsWith("Bearer ")) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } // 满足条件则继续执行后续过滤器或路由处理器 return chain.filter(exchange); } } ``` 3. 在 Spring Boot 应用程序中,将自定义过滤器注册为一个 Bean,例如: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GatewayConfig { @Bean public MyFilter myFilter() { return new MyFilter(); } } ``` 这样,在配置文件中指定的路由上会应用自定义的过滤器。你可以根据实际需求编写不同的过滤器类来实现各种局部过滤器的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值