一、什么是全局过滤器🍉
首先,我们要知道全局过滤器其实是特殊路由过滤器(特殊的GatewayFilter),会有条件地作用于所有路由。
为什么要自定义全局过滤器?就好比是看大门的保安大叔,平时主要是做好进出大门外来人员登记即可,但是因为新冠疫情,现在还需要给外来人员测量体温等等。而已有的全局过滤器就好比是登记操作,而自定义的全局过滤器就好比是测量体温操作,是结合具体场景添加的。
比如以下场景:
- 认证判断
- 权限校验
- 黑白名单
- 跨域配置
工作原理:
当请求与路由匹配时,过滤WebHandler会将GlobalFilter的所有实例和GatewayFilter的所有特定于路由的实例添加到过滤器链中。该组合的过滤器链由org.springframework.core.Ordered接口排序,您可以通过实现getOrder()方法进行设置。
由于Spring Cloud Gateway区分了过滤器逻辑执行的“前”阶段和“后”阶段,因此具有最高优先级的过滤器在“前”阶段中是第一个,在“后”阶段中是最后一个。
二、自带的全局过滤器包含哪些🍉
要自定义全局过滤,首先我们应该去了解一下gateway自带的主要的全局过滤器有哪些,并了解他们的作用。
- ForwardRoutingFilter
ForwardRoutingFilter在exchange属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR中查找URI。如果URL具有转发scheme(例如forward:/// localendpoint),则它将使用Spring DispatcherHandler来处理请求。请求URL的路径部分被转发URL中的路径覆盖。未经修改的原始URL会附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性中的列表中。
- LoadBalancerClientFilter
LoadBalancerClientFilter在ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的exchange属性中查找URI。如果URL的方案为lb(例如lb:// myservice),它将使用Spring Cloud LoadBalancerClient将名称(在本例中为myservice)解析为实际的主机和端口,并替换同一属性中的URI。未经修改的原始URL会附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性中的列表中。过滤器还会在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR属性中查找其是否等于lb。如果是,则适用相同的规则。下面的清单配置一个LoadBalancerClientFilter:
spring:
cloud:
gateway:
routes:- id: myRoute
uri: lb://service
predicates:-Path=/service/**
注意1:默认情况下,当在LoadBalancer中找不到服务实例时,将返回503。您可以通过设置spring.cloud.gateway.loadbalancer.use404 = true将网关配置为返回404。
注意2:从LoadBalancer返回的ServiceInstance的isSecure值将覆盖对网关的请求中指定的方案。例如,如果请求通过HTTPS进入网关,但是ServiceInstance指示它不安全,则下游请求通过HTTP发出。相反的情况也可以适用。但是,如果在网关配置中为路由指定了GATEWAY_SCHEME_PREFIX_ATTR,则会删除前缀,并且路由URL产生的方案将覆盖ServiceInstance配置。
注意3:LoadBalancerClientFilter在默认情况下使用阻塞的LoadBalancerClient。建议改用ReactiveLoadBalancerClientFilter。可以通过将spring.cloud.loadbalancer.ribbon.enabled的值设置为false来切换到该值。
- ReactiveLoadBalancerClientFilter
ReactiveLoadBalancerClientFilter与上面的LoadBalancerClientFilter类似,差异主要是ReactiveLoadBalancerClientFilter是非阻塞的。
注意1:与LoadBalancerClientFilter一样,默认情况下,当ReactorLoadBalancer无法找到服务实例时,将返回503。您可以通过设置spring.cloud.gateway.loadbalancer.use404 = true将网关配置为返回404。
- NettyRoutingFilter
如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的exchange属性中的URL带有http或https,则将运行NettyRoutingFilter。它使用Netty HttpClient发出下游代理请求。响应将放入ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR的exchange属性中,供后面的过滤器使用。 (还有一个实验性的WebClientHttpRoutingFilter,它执行相同的功能,但不需要Netty。)
经过该过滤器时,会通过ServerWebExchangeUtils.setAlreadyRouted方法把exchange对象标记为“已路由”。
- RouteToRequestUrlFilter
如果ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR的exchange属性中存在Route对象,则RouteToRequestUrlFilter将运行。它基于请求URI创建一个新URI,但使用Route对象的URI属性进行更新。新的URI放置在ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的exchange属性中。
如果URI具有scheme前缀(例如lb:ws://serviceid),则将从URI中剥离lb方案,并将其放在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR中,以供后面在过滤器链中使用。
- WebsocketRoutingFilter
顾名思义,WebsocketRoutingFilter过滤器是用于处理websocket请求的,如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的exchange属性中的URL具有ws或wss的schema,则将运行WebsocketRoutingFilter。它使用Spring WebSocket基础结构向下游转发websocket请求。
您可以通过为URI加上lb前缀来平衡websocket的负载,例如lb:ws://serviceid
经过该过滤器时,会通过ServerWebExchangeUtils.setAlreadyRouted方法把exchange对象标记为“已路由”。
配置示例如下,通常情况下,除了配置ws,还需要配置一个http的,因为发起websocket连接的请求是http请求。
# 官方示例
spring:
cloud:
gateway:
routes:
# SockJS route
- id: websocket_sockjs_route
uri: http://localhost:3001
predicates:-Path=/websocket/info/**
# Normal Websocket route
- id: websocket_route
uri: ws://localhost:3001
predicates:
- Path=/websocket/**
- GatewayMetricsFilter
该过滤器主要用于做网关度量监控的,要启用,需添加spring-boot-starter-actuator依赖。然后,默认情况下,只要属性spring.cloud.gateway.metrics.enabled未设置为false,GatewayMetricsFilter就会运行。此过滤器添加一个带有以下标记的计时器度量标准,名为gateway.requests:
- routeId: 路由ID.
- routeUri: 需要路由的API
- outcome: 结果分类参考 HttpStatus.Series.
- status: 返回给客户端的http status
- httpStatusCode: 返回给客户端的http status
- httpMethod: 用户请求的http Method 这些指标随后可从/actuator/metrics/gateway.requests中进行抓取,并可轻松地与Prometheus集成以创建Grafana dashboard。
要启用prometheus端点,请添加micrometer-registry-prometheus依赖。
三、如何自定义全局过滤器🍉
在这里演示过滤黑白名单
1.在配置文件中定义黑白名单🥝
#配置端口号
server:
port:81
#服务名称
spring:
application:
name: qy165-lzq
#配置路径规则
cloud:
gateway:
routes:- id: lzq01 #路由的唯一标识-------随便起只要不重复就可以 如果不写默认uuid创建
uri: http://localhost:8001 # 真是转发地址
predicates: # 断言 当满足当前条件时进行跳转到uri 可以理解为判断
-Path=/aaa/**
- id: lzq02
uri: lb://lzq-01 #lb 表示跳转方式 获取nacos上的服务器名
predicates:
- Path=/aaa/**
#连接nacos地址
nacos:
discovery:
server-addr: localhost:8848
register-enabled: false
filter:
whitePaths:
- "/product/login"
- "/product/aaa"
- "/product/bbb"
blackPaths:
- "/product/ccc"
2.定义工具类读取黑白名单🥝
packagecom.lzq.filter;importlombok.Data;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;importjava.util.HashSet;importjava.util.Set;@Component@ConfigurationProperties(prefix ="filter")@DatapublicclassFilterUrl{privateSet<String> whitePaths=newHashSet<>();privateSet<String> blackPaths=newHashSet<>();}
3.编写过滤器🥝
定义实体类实现接口
packagecom.lzq.filter;importcom.alibaba.fastjson.JSON;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.Ordered;importorg.springframework.core.io.buffer.DataBuffer;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.http.server.reactive.ServerHttpResponse;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;importjava.nio.charset.StandardCharsets;importjava.util.HashMap;importjava.util.Map;@ComponentpublicclassLogFilterimplementsGlobalFilter,Ordered{@AutowiredprivateFilterUrl filterUrl;@OverridepublicMono<Void>filter(ServerWebExchange exchange,GatewayFilterChain chain){ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();//过滤的业务代码//1.获取请求路径---String path = request.getPath().toString();//判断该路径是否放行路径if(filterUrl.getWhitePaths().contains(path)){return chain.filter(exchange);}//获取请求头String token = request.getHeaders().getFirst("token");if(StringUtils.hasText(token)&&"admin".equals(token)){//查看redis中是否存在该tokenreturn chain.filter(exchange);//放行}//json数据Map<String,Object> map =newHashMap<>();
map.put("msg","未登录");
map.put("code",403);//3.3作JSON转换byte[] bytes =JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);//3.4调用bufferFactory方法,生成DataBuffer对象DataBuffer buffer = response.bufferFactory().wrap(bytes);//4.调用Mono中的just方法,返回要写给前端的JSON数据return response.writeWith(Mono.just(buffer));}//Ordered:优先级 值越小 优先级越高@OverridepublicintgetOrder(){return0;}}
4.解决跨域问题🥝
第一种方式创建实体类
packagecom.lzq.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.reactive.CorsWebFilter;importorg.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;importorg.springframework.web.util.pattern.PathPatternParser;importjava.util.Date;@ConfigurationpublicclassCorConfig{//处理跨域@BeanpublicCorsWebFiltercorsFilter(){CorsConfiguration config =newCorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");UrlBasedCorsConfigurationSource source =newUrlBasedCorsConfigurationSource(newPathPatternParser());
source.registerCorsConfiguration("/**", config);returnnewCorsWebFilter(source);}}
第二种方式在配置文件中编写
server:
port:81
spring:
application:
name: gateway #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos地址
gateway:
globalcors:
add-to-simple-url-handler-mapping:true
cors-configurations:'[/**]': #拦截的请求
allowedOrigins: #允许跨域的请求
-"http://localhost:8080"
allowedMethods: #运行跨域的请求方式
-"GET"-"POST"-"DELETE"-"PUT"-"OPTIONS"
allowedHeaders:"*" #允许请求中携带的头信息
allowedCredentials:true #是否允许携带cookie
maxAge:36000 #跨域检测的有效期,单位s
版权归原作者 低调的黑客 所有, 如有侵权,请联系我们删除。