# 概述

当需要拦截请求进行处理时,可通过 fizz 插件机制实现。

fizz 插件:

1、类似 spring 的 WebFilter(也可按 servlet 规范中的 filter 理解,只不过是非阻塞式的)。 2、对不同的请求,可配置不同的上下文参数,可通过管理后台完成配置。

# 开发规范

一个插件对应一个工程、一个仓库,并以 jar 包的形式发布,然后可被 fizz 网关依赖,参 https://github.com/wehotel/fizz-demo-plugin,新插件可以此为模板进行开发,其本身也是个 fizz 网关,可直接在工程内进行插件测试,下面具体说明。

1、申请插件仓库

申请方提供插件名称,比如上面的演示插件 demo,官方据此创建名为 fizz-demo-plugin 的仓库,申请方在仓库下开发插件,fizz-demo-plugin 也是插件的正式名称。

2、仓库 pom

可复制 fizz-demo-plugin 的 pom,调整:

    <groupId>com.fizzgate</groupId>
    <artifactId>fizz-demo-plugin</artifactId>
    <version>2.3.0-SNAPSHOT</version>

artifactId 为正式插件名称,version 为相应版本即可。

ps: 后续调整下面的 exclude,去掉与插件无关的内容

<plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-jar-plugin</artifactId>
     <configuration>
         <excludes>
             <exclude>*.xml</exclude>
             <exclude>*.yml</exclude>
             <exclude>we/plugin/demo/DemoApiConfig**</exclude>
             <exclude>we/plugin/demo/Main**</exclude>
         </excludes>
     </configuration>
</plugin>

3、src/resources

可直接复制 fizz-demo-plugin 的配置,调整 application.yml 中的 aggregate.redis 为申请方的 redis。

4、src/java

工程根包命名规则为 we.plugin.插件名,如上面的 demo 插件工程,根包为 we.plugin.demo。

5、插件逻辑

实现 FizzPluginFilter 即是一个 fizz 插件,如 fizz-demo-plugin 中的 DemoPluginFilter,其功能是拦截请求后输出 "this is demo plugin"。

fizz-demo-plugin 工程本身也是个 fizz 网关,默认监听 8600 端口。

DemoApiConfig.java(仅用于本工程测试) 配置了一个反向代理路由,并绑定了 demoPlugin(DemoPluginFilter)插件,请求 http://127.0.0.1:8600/proxy/xservice/ypath,网关将执行 DemoPluginFilter,然后转发请求到 http://127.0.0.1:9094/@ypath,此接口需已存在并启动,发起 http://127.0.0.1:8600/proxy/xservice/ypath 的方法和 http://127.0.0.1:9094/@ypath 支持的方法,一致即可,路由配置中并无限制。

Main.java(仅用于本工程测试) ,运行它,然后发起 http://127.0.0.1:8600/proxy/xservice/ypath 请求即可测试插件。

另外一种测试方式是 mvn install fizz-demo-plugin,在类似 fizz-gateway-node 的 fizz-bootstrap 网关工程中引入并测试,install jar 注意调整:

<plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-jar-plugin</artifactId>
     <configuration>
         <excludes>
             <exclude>*.xml</exclude>
             <exclude>*.yml</exclude>
             <exclude>we/plugin/demo/DemoApiConfig**</exclude>
             <exclude>we/plugin/demo/Main**</exclude>
         </excludes>
     </configuration>
</plugin>

去掉配置和测试内容。

6、插件发布

由官方统一发布。

上面介绍插件开发的规范,下面的 gateway 开发、manager 配置两部分,以一个例子介绍插件逻辑的开发。

例子:假设用户登录系统后,系统生成对应的 token,以 token (key) -> 用户 id (value) 的形式存储于 redis,用户的后续请求需带上 token,否则无法访问系统逻辑,若所带 token 在系统中不存在,也拒绝访问。

# gateway开发

实现

public interface FizzPluginFilter {
    public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config);
}

即定义了一个插件,config接收管理后台(manager)配置的参数。

例子插件:

@Component(RedisAuthPlugin.REDIS_AUTH_PLUGIN) // 插件id,必须
public class RedisAuthPlugin implements FizzPluginFilter {

    private static final Logger log = LoggerFactory.getLogger(RedisAuthPlugin.class);

    public static final String REDIS_AUTH_PLUGIN = "redisAuthPlugin"; // 管理后台据此设置插件id

    @Resource(name = RedisConfig.REACTIVE_STRING_REDIS_TEMPLATE)
    private ReactiveStringRedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
        String customConfig = (String) config.get(PluginConfig.CUSTOM_CONFIG); // 获取管理后台自定义的插件配置
        log.info("custom plugin config: " + customConfig);
        doSomething();
        
        String tk = exchange.getRequest().getQueryParams().getFirst("token"); // 获取客户端请求携带的 token
        return
                redisTemplate.opsForValue().get(tk).defaultIfEmpty(Constants.Symbol.EMPTY) // 检查 redis 中是否存在 tk
                        .flatMap(
                                user -> {
                                    if (user == Constants.Symbol.EMPTY) {
                                        return WebUtils.buildDirectResponse(exchange, HttpStatus.OK, null, "不存在 token " + tk); // 拒绝当前请求
                                    } else {
                                        exchange.getAttributes().put("11", "22"); // 往后续插件或逻辑传递参数
                                        Mono next = FizzPluginFilterChain.next(exchange); // 执行下一个插件或后续逻辑
                                        return next.defaultIfEmpty(ReactorUtils.NULL).flatMap(
                                                nil -> {
                                                    doAfterNext(); // 当 next 完成时执行一些逻辑
                                                    return Mono.empty();
                                                }
                                        );
                                    }
                                }
                        );
    }

    public void doSomething() {
    }

    public void doAfterNext() {
        log.info("do after next plugin done");
    }
}

例子代码:https://github.com/wehotel/fizz-examples/tree/master/fizz-example-plugin/redis-auth-plugin,例子的运行等,参其中的 README.md,为方便演示,例子不对接管理后台,相关参数配在例子代码中。

# manager配置

1、新增插件定义

2、应用插件

编辑路由:

在RedisAuthPlugin中,可通过config.get("param6") 访问参数6