一、功能介绍
设备密钥认证使用平台为每台设备生成的独立设备密钥(DeviceSecret)验证设备身份。设备使用 DeviceSecret 对 MQTT Username 进行 HMAC 签名,并将签名结果组成 Password;平台校验通过后,设备即可上线。
注意:这篇中的 DeviceSecret 也称为 PSK。DeviceSecret 属于敏感凭证,请妥善保管。
如果尚未确定使用设备密钥认证还是设备证书认证,请参见 认证方式概览。
二、认证流程
设备密钥认证流程参见下图:
三、认证参数
如需在控制台查看并复制设备身份参数及 MQTT 连接参数,请参见 获取设备三元组。
四、设备身份参数(设备三元组)
| 参数 | 参数来源 | 说明 | 是否保密 |
| ProductID | 产品详情页或设备详情页。 | 标识设备所属产品。 | 否 |
| DeviceName | 设备详情页。 | 标识产品下的具体设备。 | 否 |
| DeviceSecret | 设备详情页或动态注册。 | 用于生成 MQTT 认证签名。 | 是 |
注意:ProductSecret 是产品级密钥,主要用于动态注册等产品级操作,不能代替 DeviceSecret 进行设备 MQTT 认证。
五、MQTT 连接参数
| 参数 | 取值或生成方式 | 说明 |
| Broker Address | 广州:${ProductID}.iotcloud.tencentdevices.com曼谷:${ProductID}.ap-bangkok.iothub.tencentdevices.com | MQTT 服务器地址。 |
| Broker Port | 1883(通用)8883(TLS+PSK 加密接入) | 密钥认证设备接入端口。 |
| Client ID | ${ProductID}${DeviceName} | ProductID 与 DeviceName 直接拼接。 |
| Username | ${ClientID};${sdkappid};${connid};${expiry} | 参与设备签名计算。 |
| Password | ${token};${签名算法} | 使用 DeviceSecret 生成。 |
| KeepAlive | 0~900秒 | MQTT 连接保活时间。 |
${}表示变量,不是实际的拼接字符。连接域名和端口应以设备所属地域及控制台当前信息为准。
各扩展字段含义如下:
| 字段 | 说明 |
| sdkappid | 固定填写12010126。 |
| connid | 客户端生成的随机字符串,用于区分连接。 |
| expiry | 签名过期时间,使用 Unix 时间戳,单位为秒。 |
| token | 使用 DeviceSecret 对完整 Username 进行 HMAC 计算得到的摘要。 |
| 签名算法 | 支持hmacsha256或hmacsha1,建议使用hmacsha256。 |
六、使用 Python 连接平台
本节使用 Python 脚本连接物联网开发平台。当脚本返回认证成功且控制台设备状态显示为在线时,即表示设备密钥认证接入完成。
本示例仅验证设备认证和上线,不包含 Topic 发布、订阅及物模型消息通信。
七、前提条件
开始接入前,请确认已完成以下准备:
·已创建认证方式为密钥认证的产品。
·已在产品下创建设备。
·已获得设备的 MQTT 连接参数。
·本地环境已安装 Python 3。
·本地网络能够访问平台 MQTT 接入域名的 1883 端口。
如果需要设备在产测或首次启动阶段获取 DeviceSecret,请参见 密钥认证设备动态注册。
八、获取设备连接参数
在控制台 获取设备三元组 并复制设备身份参数及 MQTT 连接参数。
| 控制台字段 | 脚本配置项 |
| MQTT 服务器地址 | Broker Address |
| 端口号 | Broker Port |
| Client ID | Client ID |
| Username | Username |
| Password | Password |
九、准备运行环境
建议在虚拟环境中安装依赖和运行代码。
安装 Paho MQTT 客户端:
# Windows PowerShellpython -m pip install "paho-mqtt>=2.1,<3.0"# Linux && macOS Shellpython3 -m pip install "paho-mqtt>=2.1,<3.0"
Paho MQTT 是 Python MQTT 客户端库。本示例使用 MQTT 3.1.1及 Callback API VERSION2。
安装完成后,可执行以下命令确认版本:
# Windows PowerShellpython -m pip show paho-mqtt# Linux && macOS Shellpython3 -m pip show paho-mqtt# 输出中的 Version 应为 2.1.0。
十、创建连接脚本
将以下代码保存为mqtt_connect.py:
#!/usr/bin/env python3# -*- coding: utf-8 -*-import getpassimport sysfrom importlib.metadata import PackageNotFoundError, versiontry: import paho.mqtt.client as mqttexcept ImportError: mqtt = Nonedef required_input(prompt): """读取必填参数并清除首尾空格。""" value = input(prompt).strip() if not value: raise ValueError("输入内容不能为空") return valuedef get_port(): """读取并校验MQTT端口号。""" port_text = input( "端口号(直接回车使用1883):" ).strip() if not port_text: return 1883 try: port = int(port_text) except ValueError as exc: raise ValueError("端口号必须是整数") from exc if not 1 <= port <= 65535: raise ValueError("端口号必须在1~65535范围内") return portdef check_paho_version(): """检查Paho MQTT是否安装且支持Callback API VERSION2。""" install_command = ( f'"{sys.executable}" -m pip install "paho-mqtt==2.1.0"' ) if mqtt is None: raise ImportError( "未安装Paho MQTT客户端。\\n" f"请使用当前Python环境执行:\\n{install_command}" ) try: installed_version = version("paho-mqtt") except PackageNotFoundError: installed_version = "未知" if not hasattr(mqtt, "CallbackAPIVersion"): raise RuntimeError( f"当前Paho MQTT版本为{installed_version}," "不支持Callback API VERSION2。\\n" f"请使用当前Python环境执行:\\n{install_command}" ) print(f"Paho MQTT版本:{installed_version}")def main(): check_paho_version() print("\\n请输入设备详情页中的MQTT连接参数。") print("输入内容仅在本次程序运行期间使用,不会保存到文件。\\n") broker = required_input("MQTT服务器地址:") if "://" in broker: raise ValueError( "MQTT服务器地址不能包含协议前缀," "请勿输入tcp://或mqtt://" ) if any(char in broker for char in "${}"): raise ValueError( "MQTT服务器地址不能包含${}," "请将${ProductID}替换为真实ProductID" ) port = get_port() client_id = required_input("ClientId:") username = required_input("Username:") # Password输入时不会显示在终端中。 password = getpass.getpass( "Password(输入内容不会显示):" ).strip() if not password: raise ValueError("Password不能为空") def on_connect( client, userdata, connect_flags, reason_code, properties, ): if reason_code == 0: print("\\n认证成功,设备已连接物联网开发平台。") print(f"MQTT服务器:{broker}") print(f"端口号:{port}") print("请前往控制台查看设备在线状态和上下线日志。") else: print( f"\\nMQTT连接失败,返回码:{reason_code}。" "\\n请检查ClientId、Username、Password及设备状态。" ) client.disconnect() def on_connect_fail(client, userdata): print( "\\n网络连接失败,请检查以下内容:" f"\\n- MQTT服务器地址:{broker}" f"\\n- 端口号:{port}" "\\n- DNS解析及网络访问状态" ) def on_disconnect( client, userdata, disconnect_flags, reason_code, properties, ): if reason_code != 0: print(f"\\n连接异常断开,返回码:{reason_code}") client = mqtt.Client( callback_api_version=mqtt.CallbackAPIVersion.VERSION2, client_id=client_id, clean_session=True, protocol=mqtt.MQTTv311, ) client.username_pw_set( username=username, password=password, ) client.on_connect = on_connect client.on_connect_fail = on_connect_fail client.on_disconnect = on_disconnect # 不输出Username和Password等认证信息。 print("\\n连接配置:") print(f"MQTT服务器:{broker}") print(f"端口号:{port}") print(f"ClientId:{client_id}") print("\\n正在建立MQTT连接。") print("程序将保持连接,按Ctrl+C退出。") client.connect( host=broker, port=port, keepalive=60, ) try: client.loop_forever() finally: client.disconnect()if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: print("\\n用户终止连接。") sys.exit(0) except Exception as exc: print(f"\\n运行失败:{exc}") sys.exit(1)
十一、运行连接脚本
执行:
# Windows PowerShellpython mqtt_connect.py# Linux && macOS Shellpython3 mqtt_connect.py
# 运行成功后依次输入MQTT服务器地址:端口号(直接回车使用1883):ClientId:Username:Password(输入内容不会显示):
连接成功时,程序输出类似:
正在连接:******.iotcloud.tencentdevices.com:1883ClientId:******程序将保持连接,按Ctrl+C退出。认证成功,设备已连接物联网开发平台。请前往控制台查看设备在线状态。
程序会持续运行并保持 MQTT 连接。需要断开连接时,按下Ctrl+C。
十二、验证设备上线
设备发起 MQTT 连接后,如需在控制台确认设备状态及查看上下线记录,请参见 设备上线和下线。
十三、网络连接失败
检查以下内容:
·Broker Address 是否与设备详情页一致。
·端口号是否正确。
·本地 DNS 是否能够解析 MQTT 服务器域名。
·防火墙或网络策略是否限制目标域名和端口。
·相关域名是否已加入白名单。
十四、认证失败
检查以下内容:
·ClientId、Username 和 Password 是否属于同一设备。
·复制参数时是否包含多余空格或换行。
·Username 中的 expiry 是否已过期。
·是否使用了重新生成前的旧连接参数。
·Broker 中的 ProductID 是否与设备所属产品一致。
十五、出现 HMAC sign error
如果连接参数由设备自主生成,请检查:
·ProductID 和 DeviceName 是否正确。
·DeviceSecret 是否属于当前设备。
·DeviceSecret 是否完成 Base64解码。
·参与签名的 Username 是否与连接时使用的 Username 完全一致。
·HMAC 算法是否与 Password 中的算法名称一致。
·expiry 是否晚于当前设备时间。
十六、脚本连接成功,但设备未显示在线
检查:
·是否进入了正确的实例和设备。
·ClientId 是否对应当前 DeviceName。
·脚本是否仍在运行。
·上下线日志中是否存在新的连接记录。
·是否有另一个客户端使用相同 ClientId 建立连接。