ONENET MQTT设备连接
物联网设备接入平台采用双重鉴权机制:LwM2M协议设备使用IMEI认证,其他协议设备采用密钥认证。安全策略通过密钥本地计算临时Token、避免密钥传输泄露、设置Token有效期等方式保障安全。平台提供三种密钥管理方案,推荐使用本地固化密钥+临时Token方案(方案1)。Token生成算法采用HMAC签名,需包含产品ID、设备名等参数,并经过URL编码。MQTT设备连接时需通过clientId、us
一、概述
设备接入物联网平台之前,需通过身份认证,目前平台提供IMEI和设备秘钥两种鉴权方式,对于不同接入方式的设备,鉴权方式不同。
| 接入协议 | 鉴权方式 | 说明 |
|---|---|---|
| 使用LwM2M协议接入设备 | IMEI | 用户在物联网平台注册设备时录入移动设备国际识别码IMEI,在设备接入物联网平台时使用IMEI进行访问认证,请保持平台录入IMEI和设备内置信息一致 |
| 使用其他协议接入设备 | 设备秘钥 | 用户需要先在物联网平台注册设备后,获取到产品ID和设备名称,在设备接入物联网平台时,使用产品ID、设备名称及通过核心密钥计算的token进行访问认证 |
1.安全鉴权策略
对于设备秘钥鉴权方式,物联网开放平台主要通过如下方式保证访问安全:
- 避免核心密钥网络中直接传输,从而避免核心密钥在传输中泄露;
- 通过包含由非可逆算法生成的签名的token来进行身份认证,即使token被窃取,攻击者也无法通过token反向获得核心密钥;
- 鉴权参数token具有用户自定义的过期时间属性,可从时间维度降低被攻击/仿冒的风险;
2.安全鉴权方案
先明确三种方案的核心边界(我们使用的是方案1)
| 方案 | 核心特征 | 关键动作 | 密钥 / Token 角色 | 适用场景 |
|---|---|---|---|---|
| 方案 1 | 本地固化密钥 + 计算临时 Token(带有效期) | 访问者(设备)自身存密钥,本地算临时 Token,用 Token 认证 | 密钥 =“根密钥”(仅算 Token,不直接认证);Token=“临时凭证”(过期失效) | 嵌入式设备(ESP32 / 单片机)、IoT 设备接入(安全 + 防密钥泄露) |
| 方案 2 | 无密钥 + 从管理者获取临时 Token | 访问者(设备)不存任何密钥,主动向管理者(平台 / 服务端)请求 Token,用 Token 认证 | 密钥 = 管理者持有(设备无);Token = 管理者下发的 “临时通行证” | 多租户管理、第三方授权访问(设备无密钥权限) |
| 方案 3 | 持有永久密钥 + 直接生成 Token(无临时 / 有效期) | 访问者(设备)被烧写永久密钥,用密钥直接生成 Token(无有效期 / 固定有效期),用 Token 认证 | 密钥 =“直接授权凭证”(生成的 Token 无临时属性);Token=“永久凭证”(仅密钥变,Token 才变) | 简单设备、无有效期要求的场景(安全风险高,密钥泄露即永久失效) |
二、Token算法
1.Token的组成
设备秘钥鉴权需包含 产品ID + 设备名称 + 鉴权Token ,其中Token由多个参数构成。

2.关于token参数的特别说明
res使用场景说明
| 场景 | res参数格式 | 示例 | 说明 |
|---|---|---|---|
| 产品级鉴权(一型一密) | products/{产品ID} | products/123123 | 使用产品级密钥,同一产品下设备烧录相同产品证书 |
| 设备级别鉴权(一机一密) | products/{产品ID}/devices/{设备名称} | products/123123/devices/mydev | 使用设备级密钥,每台设备烧录自己的设备证书 |
sign签名算法
参数sign的生成算法为:
sign = base64(hmac_<method>(base64decode(key), utf-8(StringForSignature)))
其中:
- Key为DMP为资源分配的访问密钥(产品级、设备级均可),其作为签名算法参数之一参与签名计算,为保证访问安全,请妥善保管。
- Key参与计算前应先进行base64decode操作。
- 用于计算签名的字符串 StringForSignature的组成顺序按照参数名称进行字符串排序,以'\n'作为参数分隔,当前版本中按照如下顺序进行排序:et、method、res、version。
StringForSignature组成示例如下:
StringForSignature = et + '\n' + method + '\n' + res+ '\n' + version
注意:每个参数均为key=value格式组成,但是仅参数中的value参与计算签名的字符串 StringForSignature的组成
若token参数如下
et = 1537255523
method = sha1
res = products/dafdfadfafdaf/devices/che1
version = 2018-10-31
则用于计算签名的字符串StringForSignature为(按照et、method、res、version的顺序)
StringForSignature = "1537255523" + "\n" + "sha1"+ "\n" + "products/123123"+ "\n" + "2018-10-31"
计算出sign后,将每个参数均采用key=value的形式表示,并用'&'作为分隔符,示例如下:
version=2018-10-31&res=products/dafdfadfafdaf/devices/che1&et=1537255523&method=sha1&sign=ZjA1NzZlMmMxYzIOTg3MjBzNjYTI2MjA4Yw=
3.参数编码
token中key=value的形式的value部分需要经过URL编码,需要进行编码的特殊符号如下:
| 序号 | 符号 | 编码 |
|---|---|---|
| 1 | + | %2B |
| 2 | 空格 | %20 |
| 3 | / | %2F |
| 4 | ? | %3F |
| 5 | % | %25 |
| 6 | # | %23 |
| 7 | & | %26 |
| 8 | = | %3D |
编码后,上例中实际传输token为:
version=2018-10-31&res=products%2F123123&et=1537255523&method=sha1&sign=ZjA1NzZlMmMxYzIOTg3MjBzNjYTI2MjA4Yw%3D
4.总结
以上只需了解即可,直接调用 OneNET Token 完整生成算法函数dev_token_generate,此函数为算法全流程封装的工具函数,调用该函数你无需关注任何复杂的算法细节。
dev_token_generate(
token, // 入参1:Token存储缓冲区
SIG_METHOD_SHA256, // 入参2:签名方法(md5/sha1/sha256,匹配method参数)
TOKEN_TIMESTAMP, // 入参3:过期时间戳(unix时间,匹配et参数)
ONENET_PRODUCT_ID, // 入参4:产品ID(res的核心组成部分)
DEVICE_NAME, // 入参5:设备名称(产品级传NULL,设备级传实际设备名,匹配res格式)
ONENET_ACCESS_KEY // 入参6:访问密钥(产品级/设备级密钥,匹配sign算法的Key参数)
);
调用dev_token_generate的核心操作(3步)
1. 准备5个精准匹配算法要求的入参(Token存储缓冲区、签名方法、过期时间戳、产品 ID、设备名、访问密钥)
注意鉴权层级(产品级 / 设备级):
- 产品级鉴权(一型一密):
入参 1-4 传 实际参数;
入参 5 传 NULL,函数自动拼接res=products/{产品ID};
入参 6 传 产品密钥,适用于:单产品下所有设备通用,批量烧录便捷;
- 设备级鉴权(一机一密):
入参 1-4 传 实际参数
入参 5 传 设备名称,函数自动拼接res=products/{产品ID}/devices/{设备名};
入参 6 传 设备密钥,适用于:单设备专属,安全性更高,适合个性化鉴权;
2. 提供足够长度的 char 类型缓冲区(token)
接收函数生成的最终完整 Token 字符串(含所有参数 + URL 编码后的 sign)长度建议≥256 字节
3. 直接调用函数,无需关注内部实现
完成入参和缓冲区准备后,直接一行代码调用函数,无需手动做任何算法相关操作,函数会自动执行所有逻辑,生成的完整 Token 会直接写入你提供的token缓冲区中。
三、MQTT设备连接
1. 服务地址
设备接入支持标准MQTTV3.1.1版本,支持TLS加密,接入服务地址如下:
各省二级平台MQTT接入服务地址详见:省平台接入地址详情页
| 连接协议 | 证书 | 地址 |
|---|---|---|
| MQTT | — | mqtts.heclouds.com:1883 |
| MQTTS | 证书下载 | mqttstls.heclouds.com:8883 |
2. 产品与设备创建
其中:产品名称用户下具有唯一性
设备名称产品内具有唯一性,推荐采用设备sn、mac地址、IMEI等信息命名设备。
3. 设备安全认证
产品、设备创建时,平台为每类产品、每个设备均分配了唯一的 key,
设备登录时需要使用通过key计算出的访问token 来进行访问安全认证,见接入安全认证
设备可通过MQTT connnect报文进行登录,connect报文中三要素填写方法如下:
| 参数 | 是否必须 | 参数说明 |
|---|---|---|
| clientId | 是 | 方式1:{deviceName}?network=1&imei={imei}&msisdn={msisdn}&iccid={iccid}&imsi={imsi}&sn={sn}&mac={mac} 适用场景:蜂窝设备(4G/5G/NB-IoT)、有固定硬件标识的 WiFi / 以太网设备 方式2:{deviceName} 适用场景:纯 WiFi 单片机、无硬件标识的简易设备 说明:只有deviceName参与token的生成,其他字符不参与。详细参数说明见下方 |
| username | 是 | 平台分配的产品ID |
| password | 是 | 填写经过 key 计算的 token |
clientId参数详细说明:
| 参数 | 说明 |
|---|---|
| {deviceName} | 平台的设备名称,只有deviceName参与平台MQTT设备登录鉴权token的生成,其他字符不参与 |
| {network} | 卡的网络类型:0-非蜂窝网,1-移动,2-电信,3-联通,4-广电,1/2/3/4均为蜂窝网类型 |
| {imei} | 即IMEI,是“International Mobile Equipment Identity”的缩写,中文译为国际移动设备识别码。它是一个唯一的数字编号,用于识别蜂窝网络中的移动设备,长度15位。 |
| {msisdn} | 即MSISDN,物联卡号码,最长13位,为0~9的数字组成。 |
| {iccid} | 即ICCID,Integrate circuit card identity,集成电路卡识别码,是SIM卡的身份识别码,共有20位字符组成,以8986开头。 |
| {imsi} | 即IMSI,International Mobile Subscriber Identity,国际移动用户识别码。总长度不超过15位,为0~9的数字组成,以460开头。 |
| {sn} | 即SN,是“Serial Number”的缩写,中文译为序列号,通常用于唯一标识一个设备或产品的编号,10-32个字符,只支持数字,大小写字母及“-”或“:”。 |
| {mac} | 即MAC,是“Media Access Control”的缩写,中文译为媒体访问控制地址,是一个唯一的硬件地址,10-32个字符,只支持数字,大小写字母及“-”或“:”。 |
4.发起MQTT设备连接代码
//产品ID
#define ONENET_PRODUCT_ID "51Ws972I2v"
//设备秘钥
#define ONENET_DEVICE_KEY "dVFQb1E3QjBURm5RbFpjbVJNVmp6Z2xacVRkWVhZhnA="
//设备名称
#define ONENET_DEVICE_NAME "123"
//时间戳
#define TM_EXPIRE_TIME 1893581446
/**
* 启动mqtt连接
* @param 无
* @return 错误码
*/
esp_err_t onenet_start(void)
{
static char token[256]; // 存放 token 值
dev_token_generate(token, SIG_METHOD_SHA256, TM_EXPIRE_TIME, ONENET_PRODUCT_ID,ONENET_DEVICE_NAME, ONENET_ACCESS_KEY); // 计算 token 值(设备级)
// 1.初始化结构体(连接onenet平台参数)
esp_mqtt_client_config_t mqtt_client = {0}; //清空结构体
// 服务地址
mqtt_client.broker.address.uri = "mqtt://mqtts.heclouds.com";// mqtt地址 一定要加前缀才能解析 mqtt://
mqtt_client.broker.address.port = 1883; // mqtt端口号
// 验证信息
mqtt_client.credentials.client_id = ONENET_DEVICE_NAME; // 设备名称
mqtt_client.credentials.username = ONENET_PRODUCT_ID; // 用户名(产品id)
mqtt_client.credentials.authentication.password = token; // token值
// 将鉴权信息打印出来
ESP_LOGI(TAG,"onenet connect->\r\nclientId:%s\r\nusername:%s\r\npassword:%s\r\n",
mqtt_client.credentials.client_id,mqtt_client.credentials.username,
mqtt_client.credentials.authentication.password);
// 2.调用初始化函数,获取 MQTT 客户端句柄(发送数据、注册事件、断开连接使用)
mqtt_handle = esp_mqtt_client_init(&mqtt_client);
// 3.注册mqtt事件
esp_mqtt_client_register_event( mqtt_handle, // MQTT句柄
ESP_EVENT_ANY_ID, // 任何事件
mqtt_event_handler, // MQTT事件回调函数
NULL); // 事件回调函数参数
// 4.启动mqtt连接
return esp_mqtt_client_start(mqtt_handle); //注意:函数会创建一个mqtt任务,并不会启动mqtt连接
//mqtt连接成功后,后续所有 MQTT 操作(发布消息、订阅主题、断开连接、注销事件、重新连接等)都是用此句柄
}
更多推荐



所有评论(0)