栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

一文总结SpringCloud Gateway Filter过滤器(入门)及最佳实践 ,没有废话

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一文总结SpringCloud Gateway Filter过滤器(入门)及最佳实践 ,没有废话

푰’풎 풉풉품, 푰 풂풎 풂 품풓풂풅풖풂풕풆 풔풕풖풅풆풏풕 풇풓풐풎 푵풂풏풋풊풏품, 푪풉풊풏풂.

  •  푺풉풄풐풐풍: 푯풐풉풂풊 푼풏풊풗풆풓풔풊풕풚
  •  푳풆풂풓풏풊풏품: 푰’풎 풄풖풓풓풆풏풕풍풚 풍풆풂풓풏풊풏품 풅풆풔풊품풏 풑풂풕풕풆풓풏, 푳풆풆풕풄풐풅풆, 풅풊풔풕풓풊풃풖풕풆풅 풔풚풔풕풆풎, 풎풊풅풅풍풆풘풂풓풆 풂풏풅 풔풐 풐풏.
  •  푯풐풘 풕풐 풓풆풂풄풉 풎풆:푽푿
  •  푴풚 풃풍풐품: 풉풕풕풑풔://풉풉품풚풚풅풔.풃풍풐품.풄풔풅풏.풏풆풕/
  •  푷풓풐풇풆풔풔풊풐풏풂풍 풔풌풊풍풍풔:풎풚 풅풓풆풂풎
1-1:Filter用来干嘛的

简而言之,在目标接口运行前/后 分别施加功能。

由filter工作流程点,可以知道filter有着非常重要的作用,在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等。(目前只用到了pre类型)

1-2:Filter的生命周期


从这张图可以看出来,先进过前置pre类型的Filter之后再到具体的服务,再到post类型的Filter进行后置处理。

1-3:自定义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过滤器链
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/881612.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号