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里一個示例,很難搞清楚用法。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM