irpas技术客

Spring Cloud : Gateway 网关过滤器 GatewayFilter(四)_给自己一个smile

irpas 2199

目录

一、概述

二、过滤器

二、网关过滤器 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 根据作用范围划分为 GatewayFilter 和 GlobalFilter,二者区别如下:

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。

?

?

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

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 转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。

?

1. 自定义过滤器

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

2. 自定义网关过滤器

  自定义网关过滤器需要实现以下两个接口 :GatewayFilter,Ordered。

创建过滤器

?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 服务网关

?


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Spring #Cloud #gateway #网关过滤器 #GatewayFilter四