设备注册(一)
在本节中,我们将设计 IotHub 的设备认证机制。
设备三元组
EMQ X 在默认的情况是允许匿名连接的,所以在上一节课程中,IotDevice
类在连接 MQTT Broker 的时候没有指定 username 和 password 也能成功。
当然,我们肯定不希望任意一个设备都能连接到 Maque IotHub。首先我们需要到 Maque IotHub 注册一个设备,然后设备再通过由 Maque IotHub 下发的 username/password 连接到 Maque IotHub,以实现一机一密。
阿里云 IoT 平台用一个三元组(ProductKey, DeviceName, Secret)来标识一个逻辑上的设备,ProductKey 是指设备所属的产品,DeviceName 用来标识这个设备的唯一名称,Secret 是指这个设备连接物联网平台使用的密码。我认为这是一个很好的设计,因为即使在同一家公司内部,往往也会有多个服务不同业务的物联网产品需要接入,所以多一个 ProductKey 对后续的主题名、数据存储和分发等进行一个区分是很有必有的。
Maque IotHub 将使用类似的三元组(ProductName, DeviceName, Secret)来标识逻辑上的一台设备。ProductName由业务系统提供,可以是一个有意义的 ASCII 字符串,DeviceName 由 IotHub 自动生成,(ProductName, DeviceName)应该是全局唯一的。
这里我们约定,对一个设备(ProductName1, DeviceName1, Secret1),它接入 Maque IotHub 的 username 为 ProductName1/DeviceName1
,password 为 Secret1
。
为什么说三元组标识的是逻辑上的一台设备而不是物理上的一台设备? 比如说:移动应用接入 Maque IotHub 来订阅某个主题,假如有一个用户在多个移动设备上用同一个账号登录,他使用的应该是同一个三元组,因为他订阅的消息在每个设备上都应该能收到,那么在这种情况下一个三元组实际上是对应多个物理设备。后面我们再来讲怎么来区分物理设备。
为什么要用"/"做分隔符,这里先不作说明,在讲到下行数据处理的部分再来解释。
EMQ X 认证方式
EMQ X 通过插件提供了多种灵活的认证方式,你可以在找到 EMQ X 自带的插件列表,Maque IotHub 使用 MongoDB 作为数据存储,所以这里我们选择 。除了使用 MongoDB 认证以外,我们还会使用 JWT 的认证方式来提供一种临时性的接入认证。
在启用认证插件之前,我们需要关闭 EMQ X 的默认匿名认证。
编辑 <EMQ X 安装目录>/etc/emqx.conf
,修改以下配置项:
allow_anonymous = false
然后重新启动 EMQ X <EMQ X 安装目录>/bin/emqx restart
。
MongoDB 认证
MongoDB 的认证插件功能逻辑很简单:将设备的 username、password 存储在 MongoDB 的某个 Collection 中,当设备发起 Connect 的时候,Broker 再查找这个 Collection,如果 username/password 能匹配得上,则允许连接,否则拒绝连接。
在<EMQ X 安装目录>/etc/plugins/emqx_auth_mongo.conf
可以对 MongoDB 认证进行配置,配置项很多,在这里我们看几个关键的配置项。
- MongoDB 地址:
auth.mongo.server = 127.0.0.1:27017
。 - 用于认证的数据库:
auth.mongo.database = mqtt
存储设备 username 和 password 的数据库,这里暂时用默认值。 - 用于认证的 Collection:
auth.mongo.auth_query.collection = mqtt_user
存储设备 username 和password 的 Collection, 这里暂时使用默认值。 - password 字段名:
auth.mongo.auth_query.password_field = password
。 - password 加密方式:
auth.mongo.auth_query.password_hash = plain
, password 字段的加密方式,这里选择不加密。 - 是否打开超级用户查询:
auth.mongo.super_query = off
,设置为关闭。 - 是否打开权限查询:
auth.mongo.acl_query = off
,这里我们暂时不打开 Publish 和 Subscribe 的权限控制。
然后我们在 MongoDB 插入一条记录,在 MongoDB Shell 中运行:
use mqtt
db.createCollection("mqtt_user")
db.mqtt_user.insert({username: "test", password: "123456"})
然后加载 MongoDB 认证插件:
<EMQ X 安装目录>/bin/emqx_ctl plugins load emqx_auth_mongo
不出意外的话控制台会输出:
Start apps: [emqx_auth_mongo]
Plugin emqx_auth_mongo loaded successfully.
这个时候如果我们运行 test_mqtt.js,会得到以下输出:
Error: Connection refused: Bad username or password
接下来,我们在连接的时候指定刚才存储在 MongoDB 的 username/password: test/123456
...
var client = mqtt.connect('mqtt://127.0.0.1:1883', {
username: "test",
password: "123456"
})
...
重新运行 test_mqtt.js,如果
return code: 0
说明基于 MongoDB 的认证方式已经生效了。
如果返回的是Error: Connection refused: Bad username or password
,你需要检查:
- 插件的配置文件是否按照课程中的方式进行配置;
- MongoDB 插件是否成功加载,可通过运行
<EMQ X 安装目录>/bin/emqx_ctl plugins load emqx_auth_mongo
查看; - 对应的设备数据是否添加到 MongoDB 对应的 collection 中。
可以通过运行
<EMQ X 安装目录>/bin/emqx_ctl plugins list
方式查看插件列表,已加载的插件会显示 active=true。
JWT (JSON Web Token) 认证
使用 MongoDB 认证插件已经能够满足我们对设备注册的需求,但是我在这里还想再引入一种新的认证方式:JWT 认证。为什么呢?考虑以下两个场景:
- 在浏览器中,使用 WebSocket 方式进行接入时,你需要将接入 Maque IotHub 的 username 和 password 传给前端的 js 的代码,那么在浏览器的 Console 里就可以看见 username 和 password,这非常不安全。如果使用 JWT 认证方式,你只需要将一段有效期很短的 JWT 传给前端的 js 代码,即使泄露了,可以操作的时间窗口也很短。
- 有时候你需要绕过注册设备这个流程来连接到 Maque IotHub,EMQ X 会在一些内部的系统主题上发布与 Broker 相关的状态信息,比如连接数、消息数等。如果你需要用一个 Client 连接到 Maque IotHub 并订阅这些主题的话,先创建一个 Device 并不是很好的选择,这种情况下,用 JWT 作为一次性的密码为这些系统内部的接入做认证就会非常好。
JSON Web Token(JWT,读作 [/dʒɒt/]),是一种基于 JSON 的、用于在网络上声明某种主张的令牌(Token),更详细的介绍可以参考
EMQ X 提供了 来提供 JWT 方式的认证,在<EMQ X 安装目录>/etc/plugins/emqx_auth_jwt.conf
可以对 JWT 认证插件进行配置:
- JWT Secret:
auth.jwt.secret = emqxsecret
, 这里我们使用默认值,当然在实际生产中你需要使用一个长且复杂的字符串。 - 是否开启 Claim 验证:
auth.jwt.verify_claims = on
打开 Claim 验证。 - Claim 验证字段:
auth.jwt.verify_claims.username = %u
需要验证 Claim 中的 username 字段。
下面我们来看下如何使用 JWT 来接入 Maque IotHub:
...
var jwt = require('jsonwebtoken')
var password = jwt.sign({
username: "jwt_user",
exp: Math.floor(Date.now() / 1000) + 10
}, "emqxsecret")
var client = mqtt.connect('mqtt://127.0.0.1:1883', {
username: "jwt_user",
password: password
})
...
在这里我们使用 EMQ X 预设的 JWT Secret 签发了一个有效期为 10 秒的 JWT token 进行连接,重新运行 test_mqtt.js,如果输出为:
return code: 0
说明基于 JWT 的认证方式已经生效了。
如果返回的是Error: Connection refused: Bad username or password
,你需要检查:
- 插件的配置文件是否按照课程中的方式进行配置;
- JWT插件是否成功加载,可通过运行
<EMQ X 安装目录>/bin/emqx_ctl plugins list
查看; - 是否是使用课程中指定的 payload 来生成 JWT 的。
认证链
我们加载了 MongoDB 和 JWT 两个认证插件,EMQ X 就可以用这两个插件组成的认证链来对接入的 Client 进行认证。简单来说,设备既可以使用存储在 MongoDB 里的 username 和 password,也可以使用 JWT 来接入 EMQ X Broker。
EMQ X 在加载一个插件后,会把这个插件的名字写入
<EMQ X 安装目录>/data/loaded_plugins
, EMQ X 在每次启动时都会自动加载这个文件里面包含的插件,所以我们只需要手动加载一次这两个插件就可以了。
在这一节我们讨论了 Maque IotHub 中设备的认证方式,以及 EMQ X 是如何支持这些认证方式的。下一节我们会写一些代码,把这些功能集成到 Maque IotHub 的设备注册流程中去。
推荐阅读 👉《从 0 开始搭建 IoT 平台》
为了方便与作者交流与学习,GitChat 编辑团队组织了一个《从 0 开始搭建 IoT 平台》读者交流群,添加编辑小姐姐微信:「GitChatty6」,回复关键字「214」给编辑小姐姐获取入群资格。
