Spring Cloud - 手写 Gateway 源码,实现自定义局部 FilterFactory

目录

一、FilterFactory 分析

1.1、前置知识

1.2、分析源码

1.2.1、整体分析

1.2.2、源码分析

1.3、手写源码

1.3.1、基础框架

1.3.2、实现自定义局部过滤器

1.3.3、加参数的自定义局部过滤器器


一、FilterFactory 分析


1.1、前置知识

前面的学习我们知道,GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理,同时,springcloud 也提供了一些内置的 filter.

比如:StripPrefix,表示给请求的 url 中去表指定的 n 个前缀路由,例如 - StripPrefix=2 那么如果你原本的请求是路由是 /user/list/get ,那么经过 StripPrefix 处理后,就会变成 /get.

如果我们需要自己去实现一个像这样的局部过滤器,该怎么实现呢?

1.2、分析源码

1.2.1、整体分析

例如 StripPrefix,他继承了 AbstractGatewayFilterFactory 这个抽象类.

这里暗含了一层意思:在 application.yml 配置文件中,可以在 filters 配置里写上这个类的前缀 StripPrefix,就表示这个类(后面的 GatewayFilterFactory 是固定写法,就表示他是一个网关过滤器).

进一步的,如果我们要自定义一个局部过滤器,例如身份认证 Token 过滤器,我们就创建一个类命名为:Token + GatewayFilter,然后继承 AbstractGatewayFilterFactory 抽象类,就表示他是一个局部过滤器.

1.2.2、源码分析

源码如下:

public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactory<Config> {
    public static final String PARTS_KEY = "parts";

    public StripPrefixGatewayFilterFactory() {
        super(Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("parts");
    }

    public GatewayFilter apply(final Config config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                ServerWebExchangeUtils.addOriginalRequestUrl(exchange, request.getURI());
                String path = request.getURI().getRawPath();
                String[] originalParts = StringUtils.tokenizeToStringArray(path, "/");
                StringBuilder newPath = new StringBuilder("/");

                for(int i = 0; i < originalParts.length; ++i) {
                    if (i >= config.getParts()) {
                        if (newPath.length() > 1) {
                            newPath.append('/');
                        }

                        newPath.append(originalParts[i]);
                    }
                }

                if (newPath.length() > 1 && path.endsWith("/")) {
                    newPath.append('/');
                }

                ServerHttpRequest newRequest = request.mutate().path(newPath.toString()).build();
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
                return chain.filter(exchange.mutate().request(newRequest).build());
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts()).toString();
            }
        };
    }

    public static class Config {
        private int parts = 1;

        public Config() {
        }

        public int getParts() {
            return this.parts;
        }

        public void setParts(int parts) {
            this.parts = parts;
        }
    }
}

  • extends AbstractGatewayFilterFactory<Config> :继承 AbstractGatewayFilterFactory 表示他是一个局部过滤器.  传入一个泛型 Config(是一个静态内部类),是因为在配置 filters 的时候,可能需要给参数指定具体的值,例如 - StripPrefix=2,而 Config 就是来处理这里的 2 这个值的.
  • public static final String PARTS_KEY = "parts": 这里就是定义一个常量,后面会用上.
  • StripPrefixGatewayFilterFactory() :构造方法,需要给父类 AbstractGatewayFilterFactory 传递 Config 参数(前面分析过了),将来在 apply 方法中会用上.
  • Config:是一个静态内部类,描述了配置 filters 时,具体要给参数指定的值,并提供了 get 和 set 方法.  这个类就需要传递给父类 AbstractGatewayFilterFactory,最后回传给 apply 方法,在 apply 方法中使用.如果不想给参数指定值,就可以不写 Config 中的内容.
  • shortcutFieldOrder():这个方法是用来指定 filters 配置中参数值的顺序.  也就是说,如果 Config 个中如果有多个参数,那么你在配置 filters 时,指定参数的多个值,顺序是怎样的?他就是用来指定顺序的.
  • apply(Config config):局部过滤器的核心类,用来描述过滤规则的.  这里的参数 Config 就是刚刚讲到的 静态内部类,先传递给父类,然后再回传给了 apply 方法,之后我们就可以直接在 apply 方法中去使用 Config 类中的参数.

1.3、手写源码

1.3.1、基础框架

按照上述分析,不难写出大概模样,例如我们可以模仿源码,创建包 filter.factory ,然后在这个包下定义一个 Token 局部过滤器如下:

@Component // 表示在工厂中创建对象(不能少!)
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 核心方法: 处理过滤
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 处理过滤逻辑......
                return chain.filter(exchange);
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return super.shortcutFieldOrder();
    }

    public static class Config {

    }

}

那么我们就可以在配置文件中,添加这个自定义的局部过滤器

1.3.2、实现自定义局部过滤器

例如,自定义一个 Token 局部过滤器,那么就可以创建一个类 filter.factory.TokenGatewayFilterFactory 

在 apply 中的过滤逻辑就是,判断前端是否传入 token,如果没有就抛异常,如果有就去 redis 上看看是否存在这个 token,如果存在就放行,不存在就抛异常.

@Slf4j
@Component // 表示在工厂中创建对象
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 核心方法: 处理过滤
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //1.获取 token 信息
                //由于 header 中 key 可以重复(包括 parma 也是如此),因此获取到的是一个 List
                List<String> tokens = exchange.getRequest().getHeaders().get(RedisPrefix.TOKEN_KEY);
                if(tokens == null) {
                    throw new RuntimeException("没有 token 令牌!");
                }
                String tokenValue = tokens.get(0);
                log.info("token: {}", tokenValue);
                //2.比较 redis 上的 token 数据是否一致(redis 上存储的数据格式为: token前缀 + value)
                if(!redisTemplate.hasKey(RedisPrefix.TOKEN_KEY + tokenValue)) {
                    throw new RuntimeException("token 令牌不合法!");
                }
                return chain.filter(exchange);
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return super.shortcutFieldOrder();
    }

    public static class Config {

    }

}

a)例如 redis 存储的数据为

b)执行结果如下:

c)如果没有 token 数据,响应如下:

d)如果有 token,但是 token 值错误,响应如下:

1.3.3、加参数的自定义局部过滤器器

如果在配置 filters 的时候,要指定一些参数,例如 isRequire(boolean类型,表示是否传),name(String 类型).

那么就可以在 Config 静态内部类中描述,然后在 shortcutFieldOrder() 方法中指定顺序,最后就可以在 apply 中拿到对应的参数,如下:

@Slf4j
@Component // 表示在工厂中创建对象
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 核心方法: 处理过滤
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("config isRequire: {}", config.isRequire());
                log.info("config name: {}", config.getName());
                //1.拿到 Config 中自定义的参数 isRequire,判断是否要进行过滤
                if(config.isRequire()) {
                    //2.获取 token 信息
                    //由于 header 中 key 可以重复(包括 parma 也是如此),因此获取到的是一个 List
                    List<String> tokens = exchange.getRequest().getHeaders().get(RedisPrefix.TOKEN_KEY);
                    if(tokens == null) {
                        throw new RuntimeException("没有 token 令牌!");
                    }
                    String tokenValue = tokens.get(0);
                    log.info("token: {}", tokenValue);
                    //3.比较 redis 上的 token 数据是否一致(redis 上存储的数据格式为: token前缀 + value)
                    if(!redisTemplate.hasKey(RedisPrefix.TOKEN_KEY + tokenValue)) {
                        throw new RuntimeException("token 令牌不合法!");
                    }
                }
                return chain.filter(exchange);
            }
        };
    }

    /**
     * 指定参数值填写的顺序
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("require", "name");
    }

    /**
     * 提供 filter 配置中的参数值
     */
    public static class Config {
        private boolean require;
        private String name;

        public boolean isRequire() {
            return require;
        }

        public void setRequire(boolean require) {
            this.require = require;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

}

Ps:这里的不要命名为 isRequire ,会冲突! 

在配置文件中配置 filters:

执行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/120959.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI开源全新解码器,极大提升Stable Diffusion性能

在11月7日OpenAI的首届开发者大会上&#xff0c;除了推出一系列重磅产品之外&#xff0c;还开源了两款产品&#xff0c;全新解码器Consistency Decoder&#xff08;一致性解码器&#xff09;和最新语音识别模型Whisper v3。 据悉&#xff0c;Consistency Decoder可以替代Stabl…

CROS错误 403 preflight 预检

预检 403 响应 Response for preflight 403 forbidden 如上图&#xff0c;配置了请求接口一直报错&#xff0c;前端看了没有什么问题&#xff0c;不知道哪里报错了&#xff0c;那么可能是后端没有设置跨域。&#xff08;或者是设置了&#xff0c;但是可能需要换一种方式&#…

基于springboot实现致远汽车租赁平台管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现致远汽车租赁平台管理系统演示 摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统…

mysql 全文检索 demo

mysql5.6.7之后开始支持中文全文检索一直没用过&#xff0c;这次试试。 创建表 CREATE TABLE articles (id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,title VARCHAR (200),body TEXT,FULLTEXT (title, body) WITH PARSER ngram ) ENGINE INNODB DEFAULT CHARSETut…

【配置】如何在打包Spring Boot项目时按需使用日常、测试、预发、正式环境的配置文件

文章目录 前言1. 创建5个配置文件2. 在pom.xml文件中如下配置3. 在application.properties中加入环境变量 前言 在我们开发项目的时候&#xff0c;一般有四套环境&#xff1a;日常、测试、预发、正式。日常环境作为我们开发环境&#xff1b;测试环境给测试同学测试功能&#x…

Jenkins 部署.net core 项目 - NU1301错误

/root/.jenkins/workspace/householdess/services/host/fdbatt.monitor.HttpApi.Host/fdbatt.monitor.HttpApi.Host.csproj : error NU1301: 本地源“/root/.jenkins/workspace/householdess/​http:/x.x.x.x:9081/repository/nuget.org-proxy/index.json”不存在。 [/root/.je…

【Java 进阶篇】Java Filter 快速入门

欢迎来到这篇有关 Java Filter 的快速入门指南&#xff01;如果你是一名 Java 开发者或者正在学习 Java Web 开发&#xff0c;Filter 是一个强大的工具&#xff0c;可以帮助你管理和控制 Web 应用程序中的请求和响应。本文将向你解释 Filter 的基本概念&#xff0c;如何创建和配…

SHAP 和 LIME 解释模型

内容大纲 1、SHAP 解释器1.1 案例&#xff1a;用于预测患者肺癌1.2 案例中使用的shap解释器1.3 SHAP工作原理1.4 举例说明 2、LIME 解释器2.1 案例&#xff1a;判断法律案件胜诉可能性2.2 LIME解释器工作原理2.3 本地解释模型的训练过程2.4 举例说明1&#xff1a;新闻分类2.4 举…

智慧安防:监控防盗两不误的安防视频监控系统是什么样的?

随着社会的不断发展&#xff0c;安全问题越来越受到人们的关注&#xff0c;特别是对于居住在城市里的人们来说&#xff0c;盗窃问题是影响他们生活质量的重要因素之一。因此&#xff0c;根据市场需求&#xff0c;以监控防盗两不误的智慧监控系统得到了广泛的推广和应用。 一般…

长春理工大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;主页 | 教育漏洞报告平台 兑换价格&#xff1a;10金币 获取条件&#xff1a;提交长春理工大学任意中危或以上级别漏洞

删除word最后一页之后的空白页

最近编辑word比较多&#xff0c;有时最后一页&#xff08;最后一页内容还有可能是表格&#xff09;之后&#xff0c;还有一页空白页&#xff0c;单独按下backspace、del都删不掉&#xff0c;很让人着急。 经过查询有几种方法&#xff1a; &#xff08;1&#xff09;点击选中空…

rviz中引入SW的模型

一、SW装配图转urdf 参考链接&#xff1a;https://blog.csdn.net/weixin_45168199/article/details/105755388 这部分直接看参考链接就可以&#xff0c;主要思路如下 1、把sw中的零散零件按照机器人中连杆的分类整合成几个大零件 2、把几个大零件整合成装配体&#xff0c;并…

合肥工业大学数字逻辑实验三

** 数字逻辑 实验报告** ✅作者简介:CSDN内容合伙人、信息安全专业在校大学生🏆 🔥系列专栏 :hfut实验课设 📃新人博主 :欢迎点赞收藏关注,会回访! 💬舞台再大,你不上台,永远是个观众。平台再好,你不参与,永远是局外人。能力再大,你不行动,只能看别人成功!…

AIGPT重大升级,界面重新设计,功能更加饱满,用户体验升级

AIGPT AIGPT是一款功能强大的人工智能技术处理软件&#xff0c;不但拥有其他模型处理文本认知的能力还有AI绘画模型、拥有自身的插件库。 我们都知道使用ChatGPT是需要账号以及使用魔法的&#xff0c;实现其中的某一项对我们一般的初学者来说都是一次巨大的挑战&#xff0c;但…

JavaScript从入门到精通系列第三十一篇:详解JavaScript中的字符串和正则表达式相关的方法

文章目录 知识回顾 1&#xff1a;概念回顾 2&#xff1a;正则表达式字面量 一&#xff1a;字符串中正则表达式方法 1&#xff1a;split 2&#xff1a;search 3&#xff1a;match 4&#xff1a;replace 大神链接&#xff1a;作者有幸结识技术大神孙哥为好友&#xff0c;…

智能网联汽车有哪些信息安全场景

目录 1.车内安全通信 2.车云安全通信 3.安全启动 4.车载应用程序保护 5.入侵检测防御与日志管理系统 在聊完车载信息安全需求之后&#xff0c;势必要去看看​应用场景有哪些。根据之前的开发经验简单聊一下我知道的&#xff0c;还有很多没有讲&#xff0c;比如说车云之间具…

【QT】 Qt自定义ui控件

在使用Qt的ui设计时&#xff0c;Qt为我们提供了标准的窗口控件&#xff0c;但是在很多复杂工程中&#xff0c;标准窗口控件并不能满足所有的需求&#xff0c;这时就需要我们自定义控件。我们自定义的类既可以作为独立的窗口显示&#xff0c;又可以作为一个控件显示。 我们要实现…

MySQL 8.0 Clone 备份恢复演练

文章目录 前言1. 恢复目标2. 环境说明3. 克隆数据4. 恢复全量数据5. 注册增量日志6. 应用增量日志 后记 前言 上一篇文章中&#xff0c;我们介绍了使用 Clone 插件进行备份&#xff0c;相关的恢复流程将在本篇文章介绍。 MySQL 8.0 Clone Plugin 详解 恢复增量数据的方法&…

同创永益与国泰君安证券签署全面战略合作协议

10月24日&#xff0c;“生态赋能 智绘未来”国泰君安2023年金融科技文化节主题论坛召开&#xff0c;同创永益董事长朱柯、副总裁朱晓岚受邀出席活动&#xff0c;并与国泰君安总裁王松、首席信息官俞枫共同签署战略合作协议。双方将围绕产业研究、技术创新、人才培养等多方面方面…

Microsoft Dynamics 365 CE 扩展定制 - 7. 安全

在本章中,我们将介绍以下内容: 构建累积安全角色配置业务单元层次结构基于分层位置配置访问配置和分配字段级安全组建团队并共享设置访问团队对静止数据进行加密以满足FIPS 140-2标准管理Dynamics 365在线SQLTDE加密密钥简介 Dynamics 365是一个强大的平台,具有超过10年的良…