收起

从 0 开始搭建 IoT 平台

代码实操,揭秘大厂 IoT 平台设计之道

付强 · 物联网创业者兼 CTO
已发布 46 篇 共 46 篇
1

开发物联网应用,光会 MQTT 还不够

试读
2

如何运行 Maque IotHub

3

准备工作台

试读
4

设备注册(一)

试读
5

设备注册(二)

试读
6

设备注册(三)

试读
7

设备在线状态管理(一)

8

设备在线状态管理(二)

9

设备禁用与删除

10

设备权限管理

11

加一点扩展性

12

选择一个可扩展的上行数据处理方案

13

功能设计

14

实现(一)

15

实现(二)

16

设备状态上报

17

时序数据库

18

选择下行数据处理方案

19

功能设计

20

设备端实现

21

服务端实现(一)

22

服务端实现(二)

23

RPC 式调用(一)

24

RPC 式调用(二)

25

设备数据请求

26

NTP 服务

27

设备分组——功能设计

28

设备分组——服务端实现

29

设备分组——设备端实现

30

设备间通信

31

OTA 升级——功能设计

32

OTA 升级——服务端实现

33

OTA 升级——设备端实现

34

设备影子概览

35

设备影子——服务端实现

36

设备影子——设备端实现

37

IotHub 状态监控

38

EMQ X 的插件系统

39

我们会用到的 Erlang 特性

40

搭建开发和编译环境

41

编写 emqx-rabbitmq-hook(一)

42

编写 emqx-rabbitmq-hook(二)

43

使用 emqx-rabbitmq-hook

44

CoAP 简介

45

IotHub 接入 CoAP

46

我们学到了什么?

设备注册(一)

在本节中,我们将设计 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 认证插件。除了使用 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),更详细的介绍可以参考 jwt.io

EMQ X 提供了 JWT 认证插件来提供 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 平台》 R2Y8ju 为了方便与作者交流与学习,GitChat 编辑团队组织了一个《从 0 开始搭建 IoT 平台》读者交流群,添加编辑小姐姐微信:「GitChatty6」,回复关键字「214」给编辑小姐姐获取入群资格。

互动评论
评论
guoguo5 个月前
问题还是没有一个宏观的架构或流程图,一来就讲细节,没有直观认识。
评论
阿金1 年前
有没有 iot网关的相关设计?网关绑定设备,转发消息
评论
guoguo5 个月前
同问。很多场景,会接入不同协议的设备,往往需要一个设备网关来完成设备的适配和协议转换。而本文主要介绍设备直接接入iothub server
评论
人工进化时代2 年前
没有视频教程吗?
评论
关注提示×
扫码关注公众号,获得课程更新动态!