# 设备接入文档

HanClouds支持mqtt和http,设备可以通过mqtt接入HanClouds,也可以通过http接入。

通过mqtt接入时,不需要事先在平台上创建设备。在mqtt Connect消息中,提供sn和productKey,如果平台发现该sn在该产品下第一次出现,就会自动创建该设备。后续就可以在该mqtt长连接上上传设备数据、接受控制命令等等。

通过http,创建设备、上传数据是两个不同的api. 创建设备时,提供sn、productKey,如果该sn号在该产品下第一次出现,则平台会创建该设备,并返回设备级鉴权参数。后续使用设备级鉴权参数uploadToken向平台上传数据。

由于http是短连接,mqtt为长连接,所以只有通过mqtt连接的设备才可以接受命令控制。

控制台 (opens new window)中对应的productKey和snproductKey sn

# mqtt设备连接

HanClouds支持标准的mqtt 3.1.1,请参阅mqtt协议规范

mqtt接入时,第一步建议通过API 查询接入服务器IP,建议每次接入前都查询该IP地址。后续随着使用规模的扩大,该地址可能会有一些扩充(也可以直接使用域名mqtt.hanclouds.com,接入端口号为1883)。

在设备接入时,HanClouds主要使用所接入产品的productKey、产品级{accessKey:accessSecret}进行鉴权。在使用鉴权参数{accessKey:accessSecret}时,支持明文方式传输,也支持签名方式传输。

HanClouds支持签名接入和非签名方式通过mqtt接入。其具体区别如下:

  • 非签名接入:MQTT Connect报文消息时password属性不签名,{accessKey:accessSecret}直接在Connect消息中明文传输,Publish消息的payload在网络中明文传输。
  • 签名接入:MQTT Connect报文消息时password属性采用签名,{accessKey:accessSecret}中accessKey出现在Connect消息中,accessSecret只用于计算签名,不出现在Connect消息中;Publish报文消息的payload部分加密后传输。

控制台 (opens new window)中对应accessKey和accessSecret鉴权参数

# 连接前准备

  • 设备接入前,需要明确设备接入到哪个产品下,如果还未创建产品,请先在HanClouds控制台 (opens new window)创建产品
  • 采用一型一密:产品纬度代表一种设备类型,sn是产品下设备的唯一标识。设备接入时,需要准备对应产品的productKey{accessKey:accessSecret},设备序列号 sn 需要确定,每个设备初次接入时需要上报其设备序列号,设备序列号在产品下唯一,sn 不存在时平台自动创建设备并进行鉴权登录
  • 采用一机一密:平台创建设备时会自动分配唯一键deviceKey和设备级鉴权信息deviceSecret。设备接入时,需要准备对应设备的deviceKeydeviceSecret,设备需要提前在平台进行注册

控制台 (opens new window)中对应deviceKey和deviceSecretdeviceAuthParam

# 连接流程

设备通过mqtt协议进行设备连接流程如下图所示:

mqtt接入流程图

设备mqtt接入流程图

# 1.设备与平台进行tcp连接后,发送mqtt协议的connect报文消息,需要上传自己的身份和鉴权参数

connect报文消息说明:

一型一密模式下:

a) 非签名接入

  • clientId: d:{productKey}:{sn}
    • 前缀d标识device,标识不带签名模式登陆
    • {productKey} 表示取平台对应productKey的值
    • {sn}为设备唯一序列号
  • userName: {productKey}
  • password: {accessKey}:{accessSecret}
    • {accessKey} 表示取平台对应产品鉴权信息在的接入鉴权Key
    • {accessSecret} 表示取平台对应产品鉴权信息在的接入鉴权Secret
  • keepAlive: > 120 秒
    • 表示mqtt 协议层心跳保活时间至少120秒
  • cleanSession: true
    • 表示mqtt 协议层cleanSession属性设置为true

b) 签名接入

  • clientId: ds:{productKey}:{sn}
    • 这里前缀ds中,d标识device,s标识signature,合起来标识设备以带签名的方式接入mqtt服务器
  • userName: {productKey}
  • password: {accessKey}:{timestamp}:nonce:{signature}
    • {accessKey}为产品级鉴权参数中设备接入鉴权参数中的accessKey
    • {timestamp}为请求当前的时间戳,单位为秒
    • nonce为随机数,建议为uuid字符串,主要用于计算加盐处理
    • {signature} = Base64(HmacSHA1(accessSecret, productKey:accessKey:nonce:sn:timestamp)), 使用HMACSHA1计算签名,秘钥为产品级接入鉴权参数中的accessSecret。签名结果采用base64编码
    • productKey:accessKey:nonce:sn:timestamp 为把对应内容用冒号连接起来组成一个字符串
    • productKey为产品的唯一标识
  • keepAlive: > 120秒
  • cleanSession: true

特别说明:当以网关的方式连接平台时

{signature} = Base64(HmacSHA1(accessSecret, productKey:accessKey:nonce:t-gateway:sn:timestamp))

productKey:accessKey:nonce:t-gateway:sn:timestamp 为把对应内容用冒号连接起来组成一个字符串

t-gateway为固定写法,标识本次以网关方式连接

c) 国密SM签名接入

  • clientId: ds-sm:{productKey}:{sn}
    • 前缀ds中,d标识device,s标识signature,合起来标识设备以带签名的方式接入mqtt服务器,sm表示签名算法使用HmacSM3
  • userName: {productKey}
  • password: {accessKey}:{timestamp}:nonce:{signature}
    • {accessKey}为产品级鉴权参数中设备接入鉴权参数中的accessKey
    • {timestamp}为请求当前的时间戳,单位为秒
    • nonce为随机数,建议为uuid字符串,主要用于计算加盐处理
    • {signature} =Base64(HmacSM3(accessSecret, productKey:accessKey:nonce:sn:timestamp)), 使用HmacSM3计算签名,秘钥为产品级接入鉴权参数中的accessSecret。签名结果采用base64编码。
    • productKey:accessKey:nonce:sn:timestamp 为把对应内容用冒号连接起来组成一个字符串。
    • productKey为产品的唯一标识
  • keepAlive: > 120秒
  • cleanSession: true

授权一型一密:

授权接入方式是在用户产品为别人的授权产品的情况下进行设备接入,参考产品授权

a) 非签名接入

  • clientId: d:{productKey}:{sn}
    • 这里前缀d标识device,标识不带签名模式登陆
  • userName: {productKey}
  • password: {auth_accessKey}:{auth_accessSecret}
    • {auth_accessKey} 授权产品鉴权接入key
    • {auth_accessSecret} 授权产品接入secret
  • keepAlive: > 120 秒
  • cleanSession: true

控制台 (opens new window)中对应auth_accessKey和auth_accessSecret鉴权参数 b) 签名接入

  • clientId: ds:{productKey}:{sn}
    • 前缀ds中,d标识device,s标识signature,合起来标识设备以带签名的方式接入mqtt服务器
  • userName: {productKey}
  • password: {auth_accessKey}:{timestamp}:nonce:{signature}
    • {auth_accessKey}为产品级鉴权参数中设备接入鉴权参数中的auth_accessKey
    • {timestamp}为请求当前的时间戳,单位为秒
    • nonce为随机数,建议为uuid字符串,主要用于计算加盐处理
    • {signature} = Base64(HmacSHA1(auth_accessSecret, productKey:auth_accessKey:nonce:sn:timestamp)), 使用HMACSHA1计算签名,秘钥为产品级接入鉴权参数中的auth_accessSecret。签名结果采用base64编码
    • productKey:auth_accessKey:nonce:sn:timestamp 为把对应内容用冒号连接起来组成一个字符串。
    • productKey为产品的唯一标识
  • keepAlive: > 120秒
  • cleanSession: true

c) 国密SM签名接入

  • clientId: ds-sm:{productKey}:{sn}
    • 前缀ds中,d标识device,s标识signature,合起来标识设备以带签名的方式接入mqtt服务器,sm表示签名算法使用HmacSM3
  • userName: {productKey}
  • password: {auth_accessKey}:{timestamp}:nonce:{signature}
    • {auth_accessKey}为产品级鉴权参数中设备接入鉴权参数中的auth_accessKey
    • {timestamp}为请求当前的时间戳,单位为秒
    • nonce为随机数,建议为uuid字符串,主要用于计算加盐处理
    • {signature} = Base64(HmacSM3(auth_accessSecret, productKey:auth_accessKey:nonce:sn:timestamp)), 使用HmacSM3计算签名,秘钥为产品级接入鉴权参数中的auth_accessSecret。签名结果采用base64编码。
    • productKey:auth_accessKey:nonce:sn:timestamp 为把对应内容用冒号连接起来组成一个字符串。
    • productKey为产品的唯一标识
  • keepAlive: > 120秒
  • cleanSession: true

一机一密:

a) 非签名接入

  • clientId: dd:{deviceKey}
    • 这里前缀dd标识device采用不带签名模式的一机一密登录登陆模式
    • {deviceKey} 表示平台设备的唯一键
  • userName: {deviceKey}
    • {deviceKey} 表示平台设备的唯一键
  • password: {devicekey}:{deviceSecret}
    • {deviceKey} 表示平台设备的唯一键
    • {deviceSecret}表示平台设备级鉴权secret
  • keepAlive: > 120 秒
  • cleanSession: true

b) 签名接入

  • clientId: dds:{deviceKey}
    • 这里前缀dd标识device采用带签名模式的一机一密登录登陆模式
  • userName: {deviceKey}
  • password: {deviceKey}:{timestamp}:nonce:{signature}
    • {timestamp}为请求当前的时间戳,单位为秒
    • nonce为随机数,建议为uuid字符串,主要用于计算加盐处理
    • {deviceKey}为设备的唯一标识
    • signature = Base64(HmacSHA1(deviceSecret, deviceKey:nonce:timestamp)), 使用HmacSHA1计算签名,秘钥为设备的deviceSecret,签名结果采用base64编码。
    • deviceKey:nonce:timestamp 为把对应内容用冒号连接起来组成一个字符串。
  • keepAlive: > 120秒
  • cleanSession: true

c) 国密SM签名接入

  • clientId: dds-sm:{deviceKey}
    • 这里前缀dd标识device采用带签名模式的一机一密登录登陆模式,sm表示签名算法使用HmacSM3
  • userName: {deviceKey}
  • password: {deviceKey}:{timestamp}:nonce:{signature}
    • {timestamp}为请求当前的时间戳,单位为秒
    • nonce为随机数,建议为uuid字符串,主要用于计算加盐处理
    • {deviceKey}为设备的唯一标识
    • {signature} = Base64(HmacSM3(deviceSecret, deviceKey:nonce:timestamp)), 使用HmacSM3计算签名,秘钥为设备的deviceSecret,签名结果采用base64编码。
    • deviceKey:nonce:timestamp 为把对应内容用冒号连接起来组成一个字符串。
  • keepAlive: > 120秒
  • cleanSession: true

特别说明:

  • 签名接入时,timestamp为当前时间戳的秒部分,服务器会校验此时间戳,如果服务器时间和此时间差距超过30分钟,服务器会拒绝本次接入
  • 每个签名signature只能使用一次,每个nonce在一段时间内也只能使用一次。当发现重复使用时服务器会拒绝本次接入
  • 授权产品通过{auth_accessKey}:{auth_accessSecret}登录设备的时,sn必须是已经注册的设备,授权产品不能自动注册设备登录

# 签名算法

签名算法目前支持HmacSha1和HmacSm3:分别使用SHA1及SM3作为哈希算法,Java语言示例如下:

  1. HmacSHA1大部分语言都自带标准库,实现如下:
  public static String signWithHmacSha1(String secret, String content) throws NoSuchAlgorithmException, InvalidKeyException {
        SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(secretKey);
        byte[] signBytes = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(signBytes);
    }
  1. HMacSm3则需要使用第三方标准实现,首先添加Maven依赖:

     <dependency>
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcprov-jdk15on</artifactId>
       <version>1.68</version>
    </dependency>
    

    代码实现如下:

     public class HmacSM3Demo {
      
       static {
          //添加三方标准算法实现
          Security.addProvider(new BouncyCastleProvider());
        }
        
       public static String signWithHmacSM3(String secret, String content) throws NoSuchAlgorithmException,InvalidKeyException{
          SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSM3");
          Mac mac = Mac.getInstance("HmacSM3");
          mac.init(secretKey);
          byte[] signBytes = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
          return Base64.getEncoder().encodeToString(signBytes);
        }
     }
    
    

# 2.HanClouds鉴权通过后broker端会给device端publish一条topic为rsp/welcome消息消息,其payload为设备身份信息和鉴权参数。

1). 如果设备第一次创建时,此时平台在鉴权通过后,会自动创建设备,生成deviceKey和对应的鉴权参数,并在此消息中反馈给设备。

在未签名模式下,反馈内容示例如下:

{
    "cmdToken": "lkJ*********EMT",
    "deviceKey": "fd98442721f64a619de742d52e911b50",
    "deviceSecret": "b6W**********Lpx",
    "queryToken": "oiC**********RFw",
    "time": 1528430431520,
    "uploadToken": "B73**********gof"
}

在签名模式下,反馈的内容如下:

{
    "cmdToken": "lkJ**********EMT",
    "deviceKey": "********21f64a619de742d52e911b50",
    "deviceSecret": "b6W**********Lpx",
    "dynamicSecret": "STsoJnI0U3tWWDR6ZytrKSrNPcWRgEO4Pksr070ELhny5wTZDgjl5aCiHUdomAgP",
    "queryToken": "oiC**********RFw",
    "time": 1528430612274,
    "uploadToken": "B73**********gof"
}

deviceSecret在鉴权模型中有描述,每个设备在创建成功后都会有一个deviceSecret。

签名模式比未签名模式多一个dynamicSecret字段且不同的签名模式加密算法也不同。具体dynamicSecret、deviceSecret如何使用,请参考数据上传章节中数据加解密说明。

# 3.设备第一次创建后时收到deviceKey和鉴权参数后,应本地保存,并向平台publish一条topic为initack/{deviceKey}的消息,告知平台其已经收到deviceKey及其鉴权参数。当后续设备再次连接时,平台再次给设备publish到topic

rsp/welcome的内容如下:

不签名时:

{
    "deviceKey": "********21f64a619de742d52e911b50",   
    "time": 1528430431520
}

签名时:

{
    "deviceKey": "********21f64a619de742d52e911b50",
    "dynamicSecret": "STsoJnI0U3tWWDR6ZytrKSrNPcWRgEO4Pksr070ELhny5wTZDgjl5aCiHUdomAgP",
    "time": 1528430612274
}

其中不包括设备级鉴权参数。

子设备复用网关的TCP链路,网关的连接过程与普通设备一致,在网关连接后进行子设备登录。

# 设备断开连接

设备通过mqtt协议发送disconnet消息后,平台将主动断开与设备连接;设备直接断开tcp连接,平台也将视作设备请求了断开连接。

网关设备断开连接时,平台将示其代理的所有子设备下线。

# http接入文档

HTTP协议接入设备主要是调用HanClouds提供的restful API,完成设备创建,数据上传等等。由于HTTP是短连接,所以每次接入都要鉴权。具体请参阅restful api文档中新增设备设备上传数据