MQTT研究之EMQ:【EMQ之HTTP认证/访问控制】
今天进行验证的逻辑是EMQ的http的Auth以及ACL的逻辑。
首先,参照HTTP插件认证配置的说明文档进行基本的配置, 我的配置内容如下:
##--------------------------------------------------------------------
## HTTP Auth/ACL Plugin
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Authentication request.
##
## Variables:
## - %u: username
## - %c: clientid
## - %a: ipaddress
## - %P: password
##
## Value: URL
auth.http.auth_req = http://10.95.177.137:8899/scc/mqtt/auth
## Value: post | get | put
auth.http.auth_req.method = post
## Value: Params
auth.http.auth_req.params = clientid=%c,username=%u,password=%P ##--------------------------------------------------------------------
## Superuser request.
##
## Variables:
## - %u: username
## - %c: clientid
## - %a: ipaddress
##
## Value: URL
auth.http.super_req = http://10.95.177.137:8899/scc/mqtt/superuser
## Value: post | get | put
auth.http.super_req.method = post
## Value: Params
auth.http.super_req.params = clientid=%c,username=%u ##--------------------------------------------------------------------
## ACL request.
##
## Variables:
## - %A: | , = sub, = pub
## - %u: username
## - %c: clientid
## - %a: ipaddress
## - %t: topic
##
## Value: URL
auth.http.acl_req = http://10.95.177.137:8899/scc/mqtt/acl
## Value: post | get | put
auth.http.acl_req.method = get
## Value: Params
auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t
这里,非常需要值得注意的是,这个http(包括其他的,例如mysql)的auth以及acl控制,都是基于插件的逻辑实现的,即依赖其他服务进行实现,基于这个服务系统的返回值,EMQ决定auth以及acl的控制。这个理解清楚了,所有的插件相关的auth和acl都好理解了。
我这里,将auth和acl的服务实现在一个springboot的web项目scc下了,为了验证逻辑,我将真实的auth或者acl控制逻辑都简化了,主要验证流程。
a) 启动auth、acl的服务scc
package com.taikang.iot.scc.loadbalance.user.controller; import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletResponse; /**
* @Author: chengsh05
* @Date: 2019/4/9 19:40
*/
@Controller
@RequestMapping("/mqtt")
public class EmqAuthHttpController { private Logger logger = Logger.getLogger(EmqAuthHttpController.class); @RequestMapping("/auth")
public void mqttAuth(String clientid, String username, String password, HttpServletResponse response) {
//auth.http.auth_req.params = clientid=%c,username=%u,password=%P
logger.info("普通用户;clientid:" + clientid + ";username:" + username + ";password:" + password);
/**
* TODO 添加认证的逻辑,控制http的返回码, 这里的用户是否存在,通常是基于数据库做的。
* HTTP 认证/鉴权 API
* 认证/ACL 成功,API 返回200
* 认证/ACL 失败,API 返回4xx
*/
response.setStatus(401);
} @RequestMapping("/superuser")
public void mqttSuperuser(String clientid, String username, HttpServletResponse response) {
//auth.http.super_req.params = clientid=%c,username=%u
logger.info("超级用户;clientid:" + clientid + ";username:" + username);
response.setStatus(401);
} @RequestMapping("/acl")
public void mqttAcl(String access, String username, String clientid, String ipaddr, String topic, HttpServletResponse response) {
//auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t
logger.info("access: " + access + ";username: " + username + ";clientid: " + clientid + "; ipaddr: " + ipaddr + ";topic: " + topic);
response.setStatus(401);
}
}
b) 首先启动emq服务端
当然要emqttd_ctl plugins load emq_auth_http这个插件(服务节点 10.95.200.12).
[tkiot@tkwh-kfcs-app2 plugins]$ emqttd_ctl plugins list
Plugin(emq_auth_clientid, version=2.3., description=Authentication with ClientId/Password, active=false)
Plugin(emq_auth_http, version=2.3.11, description=Authentication/ACL with HTTP API, active=true)
Plugin(emq_auth_jwt, version=2.3., description=Authentication with JWT, active=false)
Plugin(emq_auth_ldap, version=2.3., description=Authentication/ACL with LDAP, active=false)
Plugin(emq_auth_mongo, version=2.3., description=Authentication/ACL with MongoDB, active=false)
Plugin(emq_auth_mysql, version=2.3., description=Authentication/ACL with MySQL, active=false)
Plugin(emq_auth_pgsql, version=2.3., description=Authentication/ACL with PostgreSQL, active=false)
Plugin(emq_auth_redis, version=2.3., description=Authentication/ACL with Redis, active=false)
Plugin(emq_auth_username, version=2.3., description=Authentication with Username/Password, active=false)
Plugin(emq_coap, version=2.3., description=CoAP Gateway, active=false)
Plugin(emq_dashboard, version=2.3., description=EMQ Web Dashboard, active=true)
Plugin(emq_lua_hook, version=2.3., description=EMQ Hooks in lua, active=false)
Plugin(emq_modules, version=2.3., description=EMQ Modules, active=true)
Plugin(emq_plugin_template, version=2.3., description=EMQ Plugin Template, active=false)
Plugin(emq_recon, version=2.3., description=Recon Plugin, active=true)
Plugin(emq_reloader, version=2.3., description=Reloader Plugin, active=false)
Plugin(emq_retainer, version=2.3., description=EMQ Retainer, active=true)
Plugin(emq_sn, version=2.3., description=MQTT-SN Gateway, active=false)
Plugin(emq_stomp, version=2.3., description=Stomp Protocol Plugin, active=false)
Plugin(emq_web_hook, version=2.3., description=EMQ Webhook Plugin, active=false)
c) 然后启动一个基于mqtt的客户端
我这里是用基于paho的一个消费者(subscriber)。
package com.taikang.iot.rulee.security; import com.taikang.iot.rulee.paho.PushCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import javax.net.ssl.SSLSocketFactory;
import java.util.concurrent.ScheduledExecutorService; public class MQTTSSLConsumer {
// public static final String HOST = "tcp://127.0.0.1:61613";
// public static final String TOPIC1 = "pos_message_all";
// private static final String clientid = "client11";
// public static final String HOST = "tcp://10.95.197.1:1883";
public static final String HOST = "ssl://10.95.200.12:8883";
public static final String TOPIC1 = "taikang/rulee";
private static final String clientid = "client11";
private MqttClient client;
private MqttConnectOptions options;
private String userName = "water"; //非必须
private String passWord = "water"; //非必须
@SuppressWarnings("unused")
private ScheduledExecutorService scheduler;
private String sslPemPath = "E:\\2018\\IOT\\MQTT\\javassl\\java\\"; private void start() {
try {
// host为主机名,clientid即连接MQTT的客户端ID,一般以唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
client = new MqttClient(HOST, clientid, new MemoryPersistence());
// MQTT的连接设置
options = new MqttConnectOptions();
//-----------SSL begin--------------
SSLSocketFactory factory = OpensslHelper.getSSLSocktet(sslPemPath + "sccCA0.crt",sslPemPath +"sccDevSMP.crt",sslPemPath + "sccDevSMP.key","shihuc");
options.setSocketFactory(factory);
//-----------end of SSL ------------
// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(false);
// 设置连接的用户名
options.setUserName(userName);
// 设置连接的密码
options.setPassword(passWord.toCharArray());
// 设置超时时间 单位为秒
options.setConnectionTimeout();
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval();
// 设置重连机制
options.setAutomaticReconnect(true);
// 设置回调
client.setCallback(new PushCallback());
MqttTopic topic = client.getTopic(TOPIC1);
//setWill方法,如果项目中需要知道客户端是否掉线可以调用该方法。设置最终端口的通知消息
//options.setWill(topic, "close".getBytes(), 2, true);//遗嘱
client.connect(options);
//订阅消息
int[] Qos = {};
String[] topic1 = {TOPIC1};
client.subscribe(topic1, Qos); } catch (Exception e) {
e.printStackTrace();
}
} public static void main(String[] args) throws MqttException {
System.setProperty("javax.net.debug", "ssl,handshake");
MQTTSSLConsumer client = new MQTTSSLConsumer();
client.start();
}
}
其实,这里用什么方式不是很重要,可以是paho的客户端,也可以是mqtt.fx工具(参照我之前的博文MQTT研究之EMQ:【SSL双向验证】)
d) 结果分析
按照上述的代码进行测试,会发现,c)步骤的代码会遇到错误,表明客户端订阅接入的时候鉴权不通过。
。。。。。。
verify_data: { , , , , , , , , , , , }
***
MQTT Con: client11, WRITE: TLSv1 Change Cipher Spec, length =
*** Finished
verify_data: { , , , , , , , , , , , }
***
MQTT Con: client11, WRITE: TLSv1 Handshake, length =
MQTT Con: client11, setSoTimeout() called
MQTT Snd: client11, WRITE: TLSv1 Application Data, length =
MQTT Rec: client11, READ: TLSv1 Application Data, length =
MQTT Rec: client11, READ: TLSv1 Application Data, length =
错误的用户名或密码 (4)
at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:28)
at org.eclipse.paho.client.mqttv3.internal.ClientState.notifyReceivedAck(ClientState.java:988)
at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:145)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
将a)中的response.setStatus(401);代码调整为response.setStatus(200);再次运行c)的客户端代码,认证通过,代码执行到acl权限控制。
scc服务的输出日志(表明emq执行了auth接口,200的返回值表明成功,然后校验是否超级用户,最后acl校验,因为acl的返回值是4xx,emq认为acl失败):
-- ::05.479 INFO --- [nio--exec-] c.t.i.s.l.u.c.EmqAuthHttpController : 普通用户;clientid:client11;username:water;password:water
-- ::05.510 INFO --- [nio--exec-] c.t.i.s.l.u.c.EmqAuthHttpController : 超级用户;clientid:client11;username:water
-- ::10.362 INFO --- [nio--exec-] c.t.i.s.l.u.c.EmqAuthHttpController : access: ;username: water;clientid: client11; ipaddr: 10.95.177.137;topic: taikang/rulee
subscriber的客户端日志(acl鉴权时,emq调用scc的服务,得到401,认为订阅者没有操控权限,所以报错):
MQTT Snd: client11, WRITE: TLSv1 Application Data, length =
MQTT Snd: client11, WRITE: TLSv1 Application Data, length =
MqttException ()
at org.eclipse.paho.client.mqttv3.MqttClient.subscribe(MqttClient.java:)
at com.taikang.iot.rulee.security.MQTTSSLConsumer.start(MQTTSSLConsumer.java:)
at com.taikang.iot.rulee.security.MQTTSSLConsumer.main(MQTTSSLConsumer.java:)
MQTT Rec: client11, READ: TLSv1 Application Data, length =
补充验证:
1. 超级用户返回为200,看看acl的逻辑
package com.taikang.iot.scc.loadbalance.user.controller; import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletResponse; /**
* @Author: chengsh05
* @Date: 2019/4/9 19:40
*/
@Controller
@RequestMapping("/mqtt")
public class EmqAuthHttpController { private Logger logger = Logger.getLogger(EmqAuthHttpController.class); @RequestMapping("/auth")
public void mqttAuth(String clientid, String username, String password, HttpServletResponse response) {
//auth.http.auth_req.params = clientid=%c,username=%u,password=%P
logger.info("普通用户;clientid:" + clientid + ";username:" + username + ";password:" + password);
/**
* TODO 添加认证的逻辑,控制http的返回码, 这里的用户是否存在,通常是基于数据库做的。
* HTTP 认证/鉴权 API
* 认证/ACL 成功,API 返回200
* 认证/ACL 失败,API 返回4xx
*/
response.setStatus(200);
} @RequestMapping("/superuser")
public void mqttSuperuser(String clientid, String username, HttpServletResponse response) {
//auth.http.super_req.params = clientid=%c,username=%u
logger.info("超级用户;clientid:" + clientid + ";username:" + username);
response.setStatus(200);
} @RequestMapping("/acl")
public void mqttAcl(String access, String username, String clientid, String ipaddr, String topic, HttpServletResponse response) {
//auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t
logger.info("access: " + access + ";username: " + username + ";clientid: " + clientid + "; ipaddr: " + ipaddr + ";topic: " + topic);
response.setStatus();
}
}
然后,再次启动subscriber程序,看看scc服务的日志输出。得到下面的结果:
-- ::52.816 INFO --- [nio--exec-] c.t.i.s.l.u.c.EmqAuthHttpController : 普通用户;clientid:client11;username:water;password:water
-- ::52.832 INFO --- [nio--exec-] c.t.i.s.l.u.c.EmqAuthHttpController : 超级用户;clientid:client11;username:water
发现什么没有呢?和上面的验证(auth接口返回200,superuser接口返回401,acl返回401)对比,很显然的发现,当superuser接口返回200后,acl接口不再调用,无条件认为acl是通过的,即为有权限。 普通用户通过auth后,需要校验acl。
2. 在emq_auth_http已加载的基础上再加载emq_auth_mysql
[tkiot@tkwh-kfcs-app2 log]$ emqttd_ctl plugins list
Plugin(emq_auth_clientid, version=2.3., description=Authentication with ClientId/Password, active=false)
Plugin(emq_auth_http, version=2.3.11, description=Authentication/ACL with HTTP API, active=true)
Plugin(emq_auth_jwt, version=2.3., description=Authentication with JWT, active=false)
Plugin(emq_auth_ldap, version=2.3., description=Authentication/ACL with LDAP, active=false)
Plugin(emq_auth_mongo, version=2.3., description=Authentication/ACL with MongoDB, active=false)
Plugin(emq_auth_mysql, version=2.3.11, description=Authentication/ACL with MySQL, active=true)
Plugin(emq_auth_pgsql, version=2.3., description=Authentication/ACL with PostgreSQL, active=false)
Plugin(emq_auth_redis, version=2.3., description=Authentication/ACL with Redis, active=false)
Plugin(emq_auth_username, version=2.3., description=Authentication with Username/Password, active=false)
Plugin(emq_coap, version=2.3., description=CoAP Gateway, active=false)
Plugin(emq_dashboard, version=2.3., description=EMQ Web Dashboard, active=true)
Plugin(emq_lua_hook, version=2.3., description=EMQ Hooks in lua, active=false)
Plugin(emq_modules, version=2.3., description=EMQ Modules, active=true)
Plugin(emq_plugin_template, version=2.3., description=EMQ Plugin Template, active=false)
Plugin(emq_recon, version=2.3., description=Recon Plugin, active=true)
Plugin(emq_reloader, version=2.3., description=Reloader Plugin, active=false)
Plugin(emq_retainer, version=2.3., description=EMQ Retainer, active=true)
Plugin(emq_sn, version=2.3., description=MQTT-SN Gateway, active=false)
Plugin(emq_stomp, version=2.3., description=Stomp Protocol Plugin, active=false)
Plugin(emq_web_hook, version=2.3., description=EMQ Webhook Plugin, active=false)
@@@###》》》1) subscriber程序中的username和password配置成mysql数据库中已经存在的,会发现,http插件认证和acl服务将不会被调用。
@@@###》》》 2)若将subscriber程序中的username和password配置成mysql数据库中不存在的,会发现,http插件认证和acl服务将会被调用。
能否说明,若MySQL/Redis等基础服务认证和acl控制器和Http认证/ACL控制器同时配置的时候,EMQ优先查询MySQL/Redis服务???
实验了MySQL,是这个现象,不知其他是否如我的猜测,没有在EMQ的官方文档看到这个说明。
MQTT研究之EMQ:【EMQ之HTTP认证/访问控制】的更多相关文章
- MQTT研究之EMQ:【SSL证书链验证】
1. 创建证书链(shell脚本) 客户端证书链关系: rootCA-->chainca1-->chainca2-->chainca3 ca caCert1 caCert2 caCe ...
- MQTT研究之EMQ:【JAVA代码构建X509证书【续集】】
openssl创建私钥,获取公钥,创建证书都是比较简单的,就几个指令,很快就可以搞定,之所以说简单,是因为证书里面的基本参数配置不需要我们组装,只需要将命令行里面需要的几个参数配置进去即可.但是呢,用 ...
- MQTT研究之EMQ:【CoAP协议应用开发】
本博文的重点是尝试CoAP协议的应用开发,其中包含CoAP协议中一个重要的开源工具libcoap的安装和遇到的问题调研.当然,为了很好的将EMQ的CoAP协议网关用起来,也调研了下EMQ体系下,CoA ...
- emqtt 试用(五)emq 的用户密码认证
MQTT 认证设置 EMQ 消息服务器认证由一系列认证插件(Plugin)提供,系统支持按用户名密码.ClientID 或匿名认证. 系统默认开启匿名认证(anonymous),通过加载认证插件可开启 ...
- emqtt emq 的用户密码认证
MQTT 认证设置 EMQ 消息服务器认证由一系列认证插件(Plugin)提供,系统支持按用户名密码.ClientID 或匿名认证. 系统默认开启匿名认证(anonymous),通过加载认证插件可开启 ...
- MQTT研究之EMQ:【基础研究】
EMQ版本V2, emqttd-centos7-v2.3.11-1.el7.centos.x86_64.rpm 下载地址:http://emqtt.com/downloads/2318/centos7 ...
- MQTT研究之EMQ:【EMQX使用中的一些问题记录(1)】
issue 1. EMQX的共享订阅 EMQX是一个非常强大的物联网通信消息总线,基于EMQX开展应用开发,要注意很多配置细节问题,这里要说到的就是共享订阅以及和cleanSession之间的关系问题 ...
- MQTT研究之EMQ:【JAVA代码构建X509证书】
这篇帖子,不会过多解释X509证书的基础理论知识,也不会介绍太多SSL/TLS的基本信息,重点介绍如何用java实现SSL协议需要的X509规范的证书. 之前的博文,介绍过用openssl创建证书,并 ...
- MQTT研究之EMQ:【wireshark抓包分析】
基于上篇博文[SSL双向验证]的环境基础,进行消息的具体梳理. 环境基础信息: . 单台Linux CentOS7.2系统,安装一个EMQTTD的实例broker. . emq的版本2.3.11. . ...
随机推荐
- PTA-栈
1-1 若一个栈的输入序列为1,2,3,…,N,输出序列的第一个元素是i,则第j个输出元素是j−i−1. (2分) T F 作者: DS课程组 单位: 浙江大学 1-2 若一个栈的 ...
- oraclesql语句笔记
1. ORA-00947:Not enough values 原因:values没有写足够的值与select()中的字段对应 2.查看一张表中共有多少个字段 select count(*) from ...
- Linux的.pid文件
PID全称是Process Identification. PID是进程的代号,每个进程有唯一的PID编号.它是进程运行时系统随机分配的,并不代表专门的进程.在运行时PID是不会改变标识符的,但是你终 ...
- [Chrome] 谷歌浏览器开启开发模式仍然无法安装油猴脚本
右键 > 属性 > 起始位置 > 添加 --enable-easy-off-store-extension-install 谷歌浏览器无法安装油猴脚本:--enable-easy-o ...
- 微信小程序:首页设置方法(开发模式,使用模式)与其他相关设置
小程序开发并不愉快,许多必建的文件不会自动生成,页面之间的跳转没有快捷键,开发者工具显示区域受限……如果谁有对应的解决办法求告知…… 开始的时候每次保存代码,页面都会刷洗重新渲染一次,而且自动跳回首页 ...
- iOS跳转第三方应用举例一号店和京东
1.首先要跳转到第三方应用都需要知道第三方应用的scheme,虽然百度能得到很多,但是不乏一些新增的或者改了的,怎么获得APP的scheme,鉴于现在iTunes不好用了,介绍一个app ---app ...
- ElasticSearch CPU和内存占用高的优化记录
公司最近使用ElasticSearch作为数据报表汇总引擎.上线三个月累计数据800万,但是今天突然大面积出现查询超时,上服务器查看服务运行情况,发现cpu使用率高达300% mem 使用率也到了90 ...
- 王者荣耀交流协会final发布-第3次scrum立会
1.例会照片 成员高远博,冉华,王磊,王玉玲,任思佳,袁玥出席.拍照的是王磊同学,王超同学因参加比赛不在学校,不能出席. master:任思佳 2.时间跨度 2017年12月3日 18:00 — 18 ...
- postman中常见的错误
get请求400错误,post请求405错误 (2016-08-31 17:19:27)转载▼出现错误原因,后台接收参数part使用的是List,参数的属性对不上,传参使用的类型是String,改为p ...
- Dapp混合模型开发--Dice2win的解读
前言: 之前讲到Dapp原生态对随机函数的支持并不友好, 现在讲讲一种解决思路. 既能保证随机函数的不可预测性, 又能保证公平性, 平台和玩家都能满意. 而Dapp中的Dice2Win实现, 刚好是其 ...