Cloud Gateway过滤器
Spring Cloud Gateway Filter
前述
此前笔记提到过,Spring Cloud Gateway
根据作用范围划分为GatewayFilter
和 GlobalFilter
GatewayFilter:网关过滤器
需要通过spring.cloud.routes.filters
配置在具体路由下,只作用在当前路由上或通过`spring.cloud.default-filters``配置在全局,作用在所有路由上。
GlobalFilter :全局过滤器
不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter
包装成GatewayFilterChain
可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。
Path 路径过滤器
RewritePathGatewayFilterFactory过滤器
:可以实现URL重写,通过重写URL可以实现隐藏实际路径提高安全性,易于用户记忆和键入,易于被搜索引擎收录等优点。
RewritePath网关过滤器工厂采用路径正则表达式参数和替换参数,使用Java正则表达式来灵活地重写请求路径。
实现方式如下:
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_routh3
uri: lb://nacos-order-consumer
predicates:
- Path=/consumer/**,/api-gateway/**
filters:
#将/api-gateway/consumer/1 重写为/consumer/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
PrefixPathGatewayFilterFactory过滤器:
PrefixPath
网关过滤器工厂为匹配的URI添加指定前缀。
gateway:
routes:
- id: payment_routh3
uri: lb://nacos-order-consumer
predicates:
- Path=/**
filters:
#将 /1 重写为 /consumer/1
- PrefixPath=/consumer
StripPrefixGatewayFilterFactory过滤器:
StripPrefix
网关过滤器工厂采用一个参数StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数。
gateway:
routes:
- id: payment_routh3
uri: lb://nacos-order-consumer
predicates:
- Path=/**
filters:
#将 /api/123/product/1 重写为 /product/1
- StripPrefix=2 # 分割几个,以斜杆计算
SetPathGatewayFilterFactory过滤器:
SetPath
网关过滤器工厂采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法,使用了SpringFramework 中的uri模板,允许多个匹配段。
gateway:
routes:
- id: payment_routh3
uri: lb://nacos-order-consumer
predicates:
- Path=/consumer/**,/api/consumer/{segment}
filters:
#将 /api/consumer/1 重写为 /consumer/1
- SetPath=/consumer/{segment}
Parameter参数过滤器
AddRequestParameter
网关过滤器工厂会将指定参数添加至匹配到的下游请求中。
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_routh3
uri: lb://nacos-order-consumer
predicates:
- Path=/consumer/**,/api-gateway/**
filters:
#将/api-gateway/consumer/1 重写为/consumer/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
- AddRequestParameter=flag,1
Status状态过滤器
Setstatus
网关过滤器工厂采用单个状态参数,它必须是有效的Spring HttpStatus
。它可以是整数404
或枚举NOT_FOUND
的字符串表示。
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_routh3
uri: lb://nacos-order-consumer
predicates:
- Path=/consumer/**,/api-gateway/**
filters:
#将/api-gateway/consumer/1 重写为/consumer/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
# 任何情况下,响应的HTTP 状态都设置为404
- Setstatus=404 # 404 或者对于的枚举 NOT_FOUND
全局过滤器GlobalFilter
/**
* @ClassName: CustomGlobalFilter
* @Description: 自定义全局过滤器进行权限验证,验证token是否合法
* @author: LiJunYi
*/
@Component
@Slf4j
public class AccessGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("####自定义全局过滤器####");
// 获取请求参数
String token = exchange.getRequest().getQueryParams().getFirst("token");
// 业务逻辑处理
if (null == token)
{
log.warn("token is null ...");
return setUnauthorizedResponse(exchange,"非法用户");
}
return chain.filter(exchange);
}
private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg)
{
ServerHttpResponse response = exchange.getResponse();
/*响应类型*/
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
/*响应状态码*/
response.setStatusCode(HttpStatus.UNAUTHORIZED);
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
return bufferFactory.wrap(JSON.toJSONBytes(AjaxResult.error(msg)));
}));
}
/**
* 过滤器执行顺序,数值越小,优先级越高
*
* @return int
*/
@Override
public int getOrder() {
return 1;
}
}
自定义全局异常处理
/**
* 用于网关的全局异常处理
* @Order(-1):优先级一定要比ResponseStatusExceptionHandler低
*/
@Slf4j
@Order(-1)
@Component
@RequiredArgsConstructor
public class GlobalErrorExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@SuppressWarnings({"rawtypes", "unchecked", "NullableProblems"})
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// JOSN格式返回
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
//todo 返回响应结果,根据业务需求,自己定制
CommonResponse resultMsg = new CommonResponse("500",ex.getMessage(),null);
return bufferFactory.wrap(objectMapper.writeValueAsBytes(resultMsg));
}
catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}