设备认证
大约 6 分钟frameworkauth
一、设备动态注册
介绍说明
物联网平台需要设备动态注册的原因主要包括以下几点:
扩展性:物联网平台通常需要支持成千上万甚至更多的设备接入。动态注册允许平台灵活地添加新设备,而无需进行复杂的预配置。
灵活性:设备可能因为各种原因需要更换或更新,动态注册使得设备更换过程更加简单,无需人工干预即可重新注册。
安全性:通过动态注册,平台可以实时验证设备的合法性,确保只有授权的设备能够接入,从而提高整个系统的安全性。
维护性:动态注册简化了设备的维护和管理过程。当设备需要更新或下线时,平台可以自动更新其注册信息。
自动化:自动化的注册流程可以减少人工操作,降低错误率,提高整个物联网系统的运行效率。
可追踪性:动态注册过程中,平台可以记录设备的注册信息,包括时间、地点等,便于进行设备管理和追踪。
适应性:随着物联网技术的发展,新的设备类型和协议可能不断出现。动态注册机制使得平台能够快速适应这些变化,无需进行大规模的系统升级。
1. 动态注册地址:
https://${域名}/auth/register/device2. 请求数据格式:
POST /auth/register/device  HTTP/1.1
Content-Type: application/x-www-form-urlencoded
productKey=JkerjK97oub&uniqueNo=U1mo8BokA&random=qaCXMpHE&sign=6ff700b87a5dd1a22a5827d2e2e179c3&signMethod=hmacmd53. 请求参数说明
| 参数 | 类型 | 说明 | 
|---|---|---|
| Method | String | POST | 
| Content-Type | String | application/x-www-form-urlencoded | 
| productKey | String | 产品唯一标识。 | 
| uniqueNo | String | 动态注册时指定的设备唯一编码(如sn、mac)。 | 
| deviceName | String | 设备名deviceName字段,留空则平台会默认生成。 | 
| random | String | 动态注册时指定的随机数。 | 
| sign | String | 根据指定的签名方法将设备信息加密后生成的签名。 | 
| signMethod | String | 签名方法,目前支持hmacmd5、hmacsha1、hmacsha256。 | 
| tenantId | String | 用户租户ID,留空则默认为000000。 | 
4. 响应数据格式:
{
    "code": 200,
    "data": {
        "productKey": "JkerjK97oub",
        "deviceName": "27OY2cgYUsW",
        "deviceSecret": "0cb56d6a63c8ffdb",
        "mqttIp": "127.0.0.1",
        "mqttPort": "1883"
    },
    "message": "success"
}5. 响应参数说明
| 参数 | 类型 | 说明 | 
|---|---|---|
| code | Integer | 结果信息,200 为成功 | 
| productKey | String | 产品唯一标识 | 
| deviceName | String | 设备名,用于设备 mqtt 认证参数 | 
| deviceSecret | String | 设备密钥,用于设备 mqtt 认证密码签名 | 
| mqttIp | String | MQTT服务器地址 | 
| mqttPort | String | MQTT服务端口 | 
6. 注册实现代码
public class DeviceRegisterTest {
    public static void main(String[] args) {
        // 产品 key 和产品密钥
        String productKey = "JkerjK97oub";
        String productSecret = "0yg2zfAgmGgaY8pS";
        // 设备唯一编号 sn 或者 mac 之类,确保不会变
        String uniqueNo1 = "U1mo8BokA";
        RegisterResp register1 = register(productKey, productSecret, uniqueNo1);
        System.out.println("设备1\t" + register1);
        String uniqueNo2 = "U1mo8BokB";
        RegisterResp register2 = register(productKey, productSecret, uniqueNo2);
        System.out.println("设备2\t" + register2);
        String uniqueNo3 = "U1mo8BokC";
        RegisterResp register3 = register(productKey, productSecret, uniqueNo3);
        System.out.println("设备3\t" + register3);
    }
    private static RegisterResp register(String productKey, String productSecret, String uniqueNo) {
        String random = StringUtil.random(8);
        
        // 签名原文从 uniqueNo->productKey->random 依次拼接而成
        // 若未传递 deviceName参数,则底层会默认生成一个随机的deviceName,另外deviceName不参与认证逻辑
        //String plainText = "deviceName" + deviceName + "uniqueNo" + uniqueNo + "productKey" + productKey + "random" + random;
        String plainText = "uniqueNo" + uniqueNo + "productKey" + productKey + "random" + random;
        
        // 签名方法
        SignMethod signMethod = SignMethod.HMAC_MD5;
        // 生成签名
        String sign = signMethod.sign(plainText, productSecret);
        // 注册请求参数
        RegisterReq req = new RegisterReq();
        // 设置产品唯一标识
        req.setProductKey(productKey);
        // 设置设备唯一标识
        req.setUniqueNo(uniqueNo);
        // 设置设备名,留空则由平台生成
        //req.setDeviceName(deviceName);
        // 设置随机值
        req.setRandom(random);
        // 设置签名
        req.setSign(sign);
        // 设置签名方法
        req.setSignMethod(signMethod.getMethod());
        // 设置租户编号,留空默认为000000
        //req.setTenantId("000000");
        // http 动态注册
        return HttpRequest.post("http://localhost:8888/auth/register/device")
            .useSlf4jLog()
            .queryMap(BeanUtil.toMap(req))
            .addHeader("Content-Type", "application/x-www-form-urlencoded")
            .execute()
            .asValue(new TypeReference<ApiResult<RegisterResp>>() {})
            .getData();
    }
}7. 注册调用结果

二、MQTT连接认证
介绍说明
在物联网的世界中,设备与平台之间的通信至关重要。MQTT协议是一种轻量级的通信协议,广泛用于设备与物联网平台之间的数据交换。与物联网平台通过MQTT协议连接通信前,设备必须与MQTT服务端进行认证,认证通过后,设备才能发布和订阅消息。
1. 连接准备
- 在设备与物联网平台通信之前,需要进行一些准备工作,主要是设备认证。认证成功后,设备才能发布和订阅消息。以下是一些关键步骤和注意事项。
 
2. 设备认证
- 设备证书共享风险:避免将同一设备证书(包括ProductKey、DeviceName和DeviceSecret)用于多个物理设备。这样做可能会导致设备频繁地上线和下线,影响通信的稳定性。
 - 通信安全:为了确保数据传输的安全,建议使用TLS加密的连接方式。
 
3. 连接参数
- 接入地址:根据物联网平台的部署情况,选择合适的域名或IP地址进行接入。
 - Keep Alive参数:在MQTT CONNECT指令中,需要设置Keep Alive(保活时间)。建议的保活时间范围是30秒到1200秒,推荐设置为300秒或更长,以适应可能的网络波动。
 - MQTT CONNECT报文:确保报文中包含正确的设备认证信息。
 
4. 设备认证方式
- 一机一密设备认证:使用设备证书(ProductKey、DeviceName和DeviceSecret)进行连接。
 - 一型一密预注册设备认证:首先从平台获取设备证书,然后按照一机一密的方式进行连接。
 
1. 连接参数如下:
{
  "clientId": "4cdc1b0d335454e0289a0e741809fe7d|clienttype=2,timestamp=1721840451485|",
  "username": "OCAu8nHFz4HNphIE&JkerjK97oub",
  "password": "0ffec555a5934551d5f8bb7b7d6bf473",
  "host": "192.168.0.1",
  "port": 1883,
  "webPort": 8083
}2. 连接参数说明:
| 参数 | 说明 | 
|---|---|
| clientId | 表示客户端ID,可自定义,长度不可超过64个字符。建议使用设备的MAC地址或SN码,方便您识别区分不同的客户端。若有租户id则在结尾处加上@租户id定义 | 
| clienttype | 标识客户端类型,0(默认值):直连设备/网关设备,1:appuser,app端用户认证, 2:web模拟器模拟设备连接认证(时间戳强制校验) | 
| signmethod | 表示签名算法类型。支持hmacmd5(默认值),hmacsha1和hmacsha256。 | 
| timestamp | 表示当前时间毫秒值,添加了此参数重连时需要重新签名。 | 
| encoding | 消息内容编码方式,支持 plain(原始)、hex(16进制)、base64。 | 
3. 连接参数示例:
| 参数 | 参数值 | 
|---|---|
| clientId | 4cdc1b0d335454e0289a0e741809fe7d@000000 | 
| deviceName | OCAu8nHFz4HNphIE | 
| deviceSecret | KtLNeGc4ZWSiTSuj8rl4ZJVmFTvV37oy | 
| productKey | JkerjK97oub | 
| timestamp | 1721840451485 | 
| signmethod | hmacmd5 | 
4. 加密参数生成逻辑:
mqttclientId=4cdc1b0d335454e0289a0e741809fe7d@000000|signmethod=hmacmd5,timestamp=1721840451485|
mqttUsername=OCAu8nHFz4HNphIE&JkerjK97oub
mqttPassword=hmacmd5("KtLNeGc4ZWSiTSuj8rl4ZJVmFTvV37oy","clientId4cdc1b0d335454e0289a0e741809fe7ddeviceNameOCAu8nHFz4HNphIEproductKeyJkerjK97oubtimestamp1721840451485";String plainText = "clientId" + clientId + "deviceName" + deviceName + "productKey" + productKey + "timestamp" + timestamp;
String mqttPassword = hmacmd5(deviceSecret, plainText);5. 连接实现代码:
@Slf4j
@RequiredArgsConstructor
public class DeviceSimulator {
    private final String productKey;
    private final String deviceName;
    private final String deviceSecret;
    /**
     * 启动模拟器
     */
    public void start() {
        // 初始化客户端认证信息
        ClientSign clientSign = createClientSign();
        String username = clientSign.getUsername();
        String password = clientSign.getPassword();
        String clientId = clientSign.getClientIdInfo().getMqttClientId();
        log.info("username: {}, password: {}, clientId: {}", username, password, clientId);
        // 初始化 MQTT 客户端
        MqttClient client = MqttClient.create()
            .username(username)
            .password(password)
            .clientId(clientId)
            .connectSync();
	}
    /**
     * 创建客户端认证信息
     *
     * @return ClientSign
     */
    private ClientSign createClientSign() {
        ClientIdInfo clientIdInfo = new ClientIdInfo();
        //设置租户id
        clientIdInfo.setTenantId("000000");
        //设置客户端id
        clientIdInfo.setClientId(deviceName);
        ClientSign clientSign = new ClientSign();
        clientSign.setClientIdInfo(clientIdInfo);
        clientSign.setProductKey(productKey);
        clientSign.setDeviceName(deviceName);
        clientSign.setDeviceSecret(deviceSecret);
        return clientSign;
    }
}6. 连接调用结果

三、MQTT认证入口
- 认证入口校验逻辑如下图所示,大家若有二开的需求可自行改造
 


