EMQ X 认证鉴权以及ACL鉴权(HTTP方式)

EMQ X document link

1 禁止匿名用户登录

sudo vim /etc/emqx/emqx.conf

1
allow_anonymous = false

restart

1
sudo emqx restart

配置之后,打开Dashboard面板测试连接
http://ip:18083/#/websocket

连接的时候没有配置用户名会发现无法连接。

2 验证MQTT CONNECT报文信息

EMQ X支持很多种验证方式,其中一种简单灵活的验证方式为HTTP接口验证。这里简单说一下MQTT协议的连接过程,MQTT基于TCP协议,在建立好一个TCP连接之后,发送的第一个报文必须是CONNECT报文,该报文中有ClientID、UserName、Password三个数据信息。MQTT服务端协议的实现需要根据自己的需要进行校验。当检验通过,MQTT服务器会返回成功的报文。当CONNECT完成,就可以进行订阅发布消息。

为了开启EMQ X的HTTP认证鉴权,我们需要在Dashboard上开启emqx_auth_http功能。

同时,我们查阅EMQ X已有的配置文件信息:/etc/emqx/plugins/emqx_auth_http.conf

1
2
3
4
5
auth.http.auth_req = http://127.0.0.1:8991/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

可以看到,默认使用了8991的端口以及POST请求。现在,我们写一个Flask程序进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask import Flask, request, Response

app = Flask(__name__)


@app.route('/mqtt/auth', methods=['POST'])
def test():
    print(request.json)
    print(request.args)
    print(request.form)
    if request.form.get('username') != 'zhangsan':
        return Response(status=403)

    return Response(status=200)


app.run(host='0.0.0.0', port=8991, debug=True)

EMQ X会提交ClientID、UserName、Password信息到我们的接口,我们可以根据自己的业务需求,动态扩展验证需求。返回200状态码代表成功。

3 控制MQTT PUBLISH和SUBSCRIBE控制报文

在CONNECT控制报文完成之后,通常用户需要订阅或者发布,在这个报文上,会携带Topic。开发者可以校验这些信息,进行发布订阅的权限控制。在EMQ X上,提供了一种HTTP接口的方式进行控制。

查看文件:/etc/emqx/plugins/emqx_auth_http.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
##--------------------------------------------------------------------
## ACL request.
##
## Variables:
##  - %A: 1 | 2, 1 = sub, 2 = pub
##  - %u: username
##  - %c: clientid
##  - %a: ipaddress
##  - %r: protocol
##  - %m: mountpoint
##  - %t: topic
##
## Value: URL
auth.http.acl_req = http://127.0.0.1:8991/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,mountpoint=%m

我们还是跟之前一样,用Flask写一个API进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from flask import Flask, request, Response

app = Flask(__name__)


@app.route('/mqtt/auth', methods=['POST'])
def test():
    print(request.json)
    print(request.args)
    print(request.form)

    # 对用户登录的信息进行校验

    if request.form.get('username') != 'zhangsan':
        return Response(status=403)

    return Response(status=200)


@app.route('/mqtt/acl', methods=['GET'])
def acl():
    print(request.args)
    # 订阅access是1
    access = request.args.get('access')
    return Response(status=200)
    # if int(access) == 1:
    #     print('返回成功')
    #     return Response(status=200)
    # else:
    #     return Response(status=403)


app.run(host='0.0.0.0', port=8991, debug=True)

同时,修改配置文件/etc/emqx/emqx.conf,使得当ACL没有匹配的我们直接拒绝

1
2
## Value: allow | deny
acl_nomatch = deny

当我们取消acl函数的注释代码,我们会发现一个现象,发布消息不会成功。但是Dashboard显示成功发送。
同样的,如果我么在access==1也就是订阅动作的时候,返回非200状态码,那么Dashboard会显示失败,重复操作,不会触发Flask的接口请求。