EMQ插件通过HTTP连接认证服务器实现认证

需求

  1. 在EMQ中添加认证插件,将到来的MQTT连接的ClientID、UserName、Password通过HTTP协议发送到认证服务器,用返回的数据决定是否允许该连接;
  2. 在连接时和断开时向服务器发送设备上线和离线信息,以支持设备管理的需要。

目前进度

3.12 基本已经掌握了插件开发的模式,但是目前发现也许可以组合几个已有的插件实现我们的需求,如果不用自己编码,虽然配置麻烦一点,但是还是能省下大量时间,初步设想是mysql认证/访问控制插件(主要采用访问控制功能)+http认证插件(加密算法认证)+webhook插件(上下线通知)

已有插件

EMQ中有一个使用http的认证插件,但使用它必须指定superuser认证服务器和访问权限服务器,不能单独开启普通用户认证功能,另外还需要集成上下线通知,我们只用到了他的三分之一功能,并且还需要额外功能,可以考虑的方案有两个,一是改写这个插件,禁用其他功能,并加上通知功能,另一个方案是自己写一个。

方案一的优点是插件部署方便,可以直接借原插件的壳,而不需要重新发布emq的整个release,缺点是代码修改很费时间,也不一定能切割出所有功能。

方案二的优点是编码简单,缺点是需要重新打包整个镜像。主要的挑战在于快速打包发布镜像。

实现路线

  1. 了解erlang基本语法,完成helloworld
  2. 编译运行EMQ的插件模板,尝试简单的修改
  3. 学习使用erlang的http库
  4. 在插件中加入http请求代码
  5. 完成简单的全流程
  6. 加入上下线hook
  7. 完成插件的config配置
  8. 整理代码,打包成独立插件并发布源码

Erlang helloworld

在erlang的官网上有一个quick start教程,半天时间基本可以看完所有关于erlang的基础语法,erlang跟golang很像,特别是并发部分,但是编写风格跟C/C++/JAVA系离得比较远,而golang则更靠近,更让我感到亲切。

EMQ插件模板

目前EMQ的插件都以独立仓库的形式开源,因此不需要clone整个emq项目,只需要clone一个插件模板就可以创建插件,而且直接在目录下make即可编译,如果增加库需要学习一下erlang库的链接配置方法。 

模板里的代码已经加载了所有钩子(hook),并且在每一个钩子处打印提示信息,插入代码非常方便。

%% Called when the plugin application start
load(Env) ->
    emqx:hook('client.connected', fun ?MODULE:on_client_connected/4, [Env]),
    emqx:hook('client.disconnected', fun ?MODULE:on_client_disconnected/3, [Env]),
    emqx:hook('client.subscribe', fun ?MODULE:on_client_subscribe/3, [Env]),
    emqx:hook('client.unsubscribe', fun ?MODULE:on_client_unsubscribe/3, [Env]),
    emqx:hook('session.created', fun ?MODULE:on_session_created/3, [Env]),
    emqx:hook('session.resumed', fun ?MODULE:on_session_resumed/3, [Env]),
    emqx:hook('session.subscribed', fun ?MODULE:on_session_subscribed/4, [Env]),
    emqx:hook('session.unsubscribed', fun ?MODULE:on_session_unsubscribed/4, [Env]),
    emqx:hook('session.terminated', fun ?MODULE:on_session_terminated/3, [Env]),
    emqx:hook('message.publish', fun ?MODULE:on_message_publish/2, [Env]),
    emqx:hook('message.delivered', fun ?MODULE:on_message_delivered/3, [Env]),
    emqx:hook('message.acked', fun ?MODULE:on_message_acked/3, [Env]),
    emqx:hook('message.dropped', fun ?MODULE:on_message_dropped/3, [Env]),

另外在acl(访问控制)的demo里有check函数,在收到连接时检查ClientID、UserName和Password,这里正是我所要的钩子,只需要在这里插入一个http的请求,通过response来判断是否允许连接就可以了。

我在插入http客户端代码的时候遇到了一个小问题:

我在init函数中增加了inets:start(),这是httpc说明中所说的初始化步骤,但是在加入这一步初始化后的http请求会发生错误,而不加入这个函数反而能正常获得response,因此我推测是emq本身早已执行了这一步初始化,因此不应当再次执行初始化。详细的原因我目前就不找了(能用就行)

后续:后面一直报相同的错,学习了一下erlang的报错机制后才发现错的是io:format()语句的参数格式,而不是因为inets:start(),以后还是要注意细节,这么小的一个问题浪费了我一整天的时间来调试,非常不值得。

Erlang HTTP client

 Erlang作为爱立信为电信产业设计的语言,在网络方面的库非常发达,http是其标准库的一部分,这里是官方的使用说明

完成简单的全流程

在解决掉上述的小小问题之后,我成功在连接接入过程中插入了向外界发送http请求的接口。

插件配置

除了代码以外,还需要将整个插件单独整理出来,并且加入简单的配置功能,否则参数直接进代码会带来很大的麻烦。

emq的配置管理用了一个叫clique的库,文档少,官方库里只有readme里一个示例,很难搞清楚用法。

 

 

posted @ 2019-03-12 17:02  bforever  阅读(2892)  评论(0编辑  收藏  举报