푰’풎 풉풉품, 푰 풂풎 풂 품풓풂풅풖풂풕풆 풔풕풖풅풆풏풕 풇풓풐풎 푵풂풏풋풊풏품, 푪풉풊풏풂.
- 푺풉풄풐풐풍: 푯풐풉풂풊 푼풏풊풗풆풓풔풊풕풚
- 푳풆풂풓풏풊풏품: 푰’풎 풄풖풓풓풆풏풕풍풚 풍풆풂풓풏풊풏품 풅풆풔풊품풏 풑풂풕풕풆풓풏, 푳풆풆풕풄풐풅풆, 풅풊풔풕풓풊풃풖풕풆풅 풔풚풔풕풆풎, 풎풊풅풅풍풆풘풂풓풆 풂풏풅 풔풐 풐풏.
- 푯풐풘 풕풐 풓풆풂풄풉 풎풆:푽푿
- 푴풚 풃풍풐품: 풉풕풕풑풔://풉풉품풚풚풅풔.풃풍풐품.풄풔풅풏.풏풆풕/
- 푷풓풐풇풆풔풔풊풐풏풂풍 풔풌풊풍풍풔:풎풚 풅풓풆풂풎
简而言之,在目标接口运行前/后 分别施加功能。
1-2:Filter的生命周期由filter工作流程点,可以知道filter有着非常重要的作用,在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等。(目前只用到了pre类型)
从这张图可以看出来,先进过前置pre类型的Filter之后再到具体的服务,再到post类型的Filter进行后置处理。
过滤器类型有Gateway Filter 和Global Filter ,前者只用在配置的目标路径上,而后者会用在配置的需要进行过滤的所有的路径之上。
注意!这里是配置的,也就是说,如果目标路径接口(比如/hello)我们都不想去过滤(没有配置),那么全局过滤器会不会在/hello上起作用?不会!这是一个坑,我尝试半天,全局不起作用的问题就在这。这一点会在下面详细给出。
1-3-1:前置配置信息我是基于nacos进行的,如果你没有nacos,也可以直接看下面的代码,适当调整也是可以通的。
- bootstrap.yml
# Tomcat
server:
port: 9527
# Spring
spring:
application:
# 应用名称
name: mylearn
main:
allow-bean-definition-overriding: true
profiles:
active: @activeProfile@
# 环境配置
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 127.0.0.1:8881
config:
# 配置中心地址
server-addr: @nacos_nginx@
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
- mylearn-dev.yml
spring:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
- id: myLearnFilters1
uri: lb://mylearn
predicates:
- Path=/mylearn123
public static final int MY_GATEWAY_FILTER_ORDER = -20;
public static final int MY_GATEWAY_FILTER2_ORDER = -19;
public static final int MY_GLOBAL_FILTER2_ORDER = -18;
}
- MyGatewayFilter:具体i的filter实现,同时实现两个接口。
@Component
public class MyGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("MyGatewayFilter works, order=" + this.getOrder());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return FilterOrderedConstant.MY_GATEWAY_FILTER_ORDER;
}
}
- FilterRoutesConfig:注册进去(实现GatewayFilter接口的,只能通过这种方式生效,不能在配置中生效,也就是说,在配置中只能用继承工厂的方法去写)
@Configuration
public class FilterRoutesConfig {
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/mylearn123
@Override
public List shortcutFieldOrder() {
return Arrays.asList(SWITCH_FLAG, NAME);
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (config.isSwitchFlag()) {
System.out.println("MyGatewayFilter2 works 配置中的第一个参数switchFlag是" + config.isSwitchFlag());
System.out.println("MyGatewayFilter2 works 配置中的第二个参数name是" + config.getName());
}
System.out.println("MyGatewayFilter2 works 过滤器前置操作, order=" + getOrder());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("过滤器后置操作");
}));
}
};
}
@Override
public int getOrder() {
return FilterOrderedConstant.MY_GATEWAY_FILTER2_ORDER;
}
static class Config {
private boolean switchFlag;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSwitchFlag() {
return switchFlag;
}
public void setSwitchFlag(boolean switchFlag) {
this.switchFlag = switchFlag;
}
}
}
- 配置:mylearn-dev.yml
spring:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
- id: myLearnFilters1
uri: lb://mylearn
predicates:
- Path=/mylearn123
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Autowired
private IgnoreWhiteProperties ignoreWhiteProperties;
@Autowired
private RedisService redisService;
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// validate white list
List whiteList = ignoreWhiteProperties.getWhites();
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (StringUtils.matches(path, whiteList)) {
return chain.filter(exchange);
}
String token = this.getTokenFromRequest(request);
Claims claims = JwtUtils.parseToken(token);
if (claims.isEmpty()) {
return unauthorizedResponse("令牌已过期或验证不正确!", exchange);
}
String redisTokenKey = JwtUtils.getUserKey(claims);
// redis中的key是UUID:loginUser对象的组合, 是否登录用redis去维护一个key
Boolean hasKey = redisService.hasKey(redisTokenKey);
if (!hasKey) {
return unauthorizedResponse("登录状态已过期", exchange);
}
String userId = JwtUtils.getUserId(claims);
String userName = JwtUtils.getUserName(claims);
if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
return unauthorizedResponse("令牌验证失败", exchange);
}
ServerHttpRequest.Builder mutate = request.mutate();
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userId);
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, userName);
addHeader(mutate, SecurityConstants.USER_KEY, redisTokenKey);
// permission,网关转发不算是内部的
removeHeader(mutate, SecurityConstants.FROM_SOURCE);
return chain.filter(exchange.mutate().request(mutate.build()).build());
}
public Mono unauthorizedResponse(String message, ServerWebExchange exchange) {
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), org.springframework.http.HttpStatus.UNAUTHORIZED, message, HttpStatus.UNAUTHORIZED);
}
public void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
if (value == null) {
return;
}
String valueStr = value.toString();
String encodeValueStr = ServletUtils.urlEncode(valueStr);
mutate.header(name, encodeValueStr);
}
public void removeHeader(ServerHttpRequest.Builder mutate, String name) {
mutate.headers(new Consumer() {
@Override
public void accept(HttpHeaders httpHeaders) {
httpHeaders.remove(name);
}
});
}
@Override
public int getOrder() {
return -200;
}
public String getTokenFromRequest(ServerHttpRequest request) {
String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
}
return token;
}
}
之后会慢慢贴出若依代码的并且讲解。
1-7:参考链接- spring cloud gateway使用 uri: lb://方式配置时,服务名的特殊要求
- 关于继承工厂的局部filter中,后面的附带参数绑定demo
- 理解Spring Cloud Gateway Filters的执行顺序(order顺序)
- spring cloud gateway之filter篇(这个挺不错的)
- Spring Cloud Gateway 源码剖析之Filter Chain过滤器链



