认证与连接

本文介绍如何进行Java Link SDK初始化,建立设备与物联网平台的连接。

前提条件

  • 已创建产品和设备。具体操作,请参见创建产品和设备

  • 已获取设备的认证信息以及设备的接入域名。

背景信息

  • Java Link SDK仅支持设备密钥方式,进行设备身份认证,具体包括以下几种认证方式。

    认证方式

    注册方式

    说明

    一机一密

    不涉及

    每台设备烧录自己的设备证书(ProductKey、DeviceName和DeviceSecret)。

    一型一密

    预注册

    • 同一产品下设备烧录相同产品证书(ProductKey和ProductSecret)。

    • 产品需开启动态注册功能。

    • 设备通过动态注册获取DeviceSecret。

    免预注册

    • 同一产品下设备烧录相同产品证书(ProductKey和ProductSecret)。

    • 产品需开启动态注册功能。

    • 设备通过动态注册获取ClientID与DeviceToken的组合。

    说明

    一型一密预注册和免预注册的区别,请参见预注册和免预注册的区别

  • Java Link SDK中参数说明,请参见LinkKitInitParams

一机一密

一机一密的设备认证方式的示例代码如下:

String productKey = "${YourProductKey}";
String deviceName = "${YourDeviceName}";
String deviceSecret = "${YourDeviceSecret}";

LinkKitInitParams params = new LinkKitInitParams();
final String TAG = "HelloWorld";

/**
 * step 1: 设置MQTT初始化参数
 */
IoTMqttClientConfig config = new IoTMqttClientConfig();

MqttConfigure.mqttHost = "{YourInstanceId}.mqtt.iothub.aliyuncs.com:8883";

/*
 *是否接受离线消息
 *对应MQTT的cleanSession字段
 */
config.receiveOfflineMsg = false;
params.mqttClientConfig = config;

/**
 * step 2: 设置初始化设备认证信息
 */
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = productKey;
deviceInfo.deviceName = deviceName;
deviceInfo.deviceSecret = deviceSecret;
params.deviceInfo = deviceInfo;

/**
 * step 3: 设置设备的username, token和clientId
 * 仅用于一型一密免预注册
 * 默认关闭
 */
 // MqttConfigure.deviceToken="${YourDeviceToken}";
 // MqttConfigure.clientId="${YourClientId}";


LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
    public void onError(AError aError) {
        ALog.e(TAG, "Init Error error= "+aError);
    }
    public void onInitDone(InitResult initResult) {
        ALog.i(TAG, "onInitDone result=" + initResult);
    }
});
说明
  • 发起初始化请求后,如果返回onInitDone表示初始化成功,返回onError表示初始化失败。

  • 初始化失败后,您可以根据业务需要,设置是否再次进行初始化,Java Link SDK不会自动尝试连接物联网平台。

  • 初始化成功后,如果设备异常断开,Java Link SDK会自动重连。

动态注册

一型一密又称动态注册,用于向物联网平台获取设备的密钥,具体分为免预注册和预注册两种方式。使用该功能前,需要确保:

  • 已在物联网平台创建产品并开启动态注册开关。

  • Demo的deviceinfo文件中的deviceSecret的值为空,productSecret不为空。

  • 请确保已执行下面示例代码中的step1、step2、step3。

  • 动态注册成功或失败之后,需要断开当前的动态注册长连接,请参考示例代码的step4。

  • 当前Demo支持的是一型一密预注册方式。您可参考示例代码中step 1的说明调整成一型一密免预注册方式。

  • 为了您的设备安全,使用一型一密认证方式获取到设备密钥后,请将设备密钥持久固化至设备。若设备需连接至物联网平台,请参考上述一机一密流程。

免预注册和预注册区别如下:

区别

预注册

免预注册

通信协议

MQTT、HTTPS

MQTT

地域支持

  • 基于MQTT的动态注册,物联网平台支持的所有地域。

  • 基于HTTPS的动态注册,仅限于上海区域,不推荐使用,不在本示例代码介绍范围之内。

华东2(上海)、华北2(北京)

返回的设备密钥

DeviceSecret具体使用方式,请参考上述一机一密示例中的Step1。

设备的ClientID和DeviceToken,请将其持久固化至设备,以便连云等其他功能使用。具体使用方式,请参考上述一机一密示例中的Step3。

添加设备

需要在物联网平台预注册设备DeviceName。

不需要在物联网平台预注册设备DeviceName。

使用次数限制

  • 同一组设备证书只能用于激活一个物理设备。若DeviceName名下已激活物理设备A,但同一产品下物理设备B需要使用该DeviceName,则您可以在物联网平台上删除设备A,使设备A的DeviceSecret作废,再使用原DeviceName重新添加设备,激活物理设备B。

  • 若设备因丢失DeviceSecret等原因需要重新激活,需您调用ResetThing接口,重置设备状态为未激活,然后将设备重新联网激活。此时,物联网平台下发的DeviceSecret不变。

物联网平台允许最多5个物理设备使用同一组ProductKey、ProductSecret、DeviceName进行激活,并为不同物理设备颁发不同的ClientID、DeviceToken。

示例代码如下:

        String deviceName = "${YourDeviceName}";
        String productKey = "${YourProductKey}";
        String productSecret = "${YourProductSecret}";

        //动态注册step1: 确定一型一密的类型(免预注册, 还是非免预注册)
        //case 1: 如果registerType里面填写了regnwl, 表明设备的一型一密方式为免预注册(即无需创建设备)
        //case 2: 如果这个字段为空, 或填写"register", 则表示为需要预注册的一型一密(需要实现创建设备)
        String registerType = "register";

        //动态注册step2: 设置动态注册的注册接入点域名
        MqttConfigure.mqttHost = "ssl://${YourMqttHostUrl}:8883";

        MqttInitParams initParams = new MqttInitParams(productKey, productSecret, deviceName, "",registerType);

        //动态注册step3: 如果用户所用的实例为新版本的公共实例或者企业实例(控制台中有实例详情的页面), 需设置动态注册的实例id
        initParams.instanceId = "${YourInstanceId}";

        final Object lock = new Object();
        LinkKit.getInstance().deviceDynamicRegister(initParams, new IOnCallListener() {
            @Override
            public void onSuccess(com.aliyun.alink.linksdk.channel.core.base.ARequest request, com.aliyun.alink.linksdk.channel.core.base.AResponse response) {
                try {
                    String responseData = new String((byte[]) response.data);
                    JSONObject jsonObject = JSONObject.parseObject(responseData);
                    // 一型一密预注册返回
                    String deviceSecret = jsonObject.getString("deviceSecret");

                    // 一型一密免预注册返回
                    String clientId = jsonObject.getString("clientId");
                    String deviceToken = jsonObject.getString("deviceToken");

                    //TODO: 请用户保存用户密钥,不要在此做连云的操作,要等step 4执行完成后再做连云的操作(例如在其onSuccess分支中进行连云)
                    
                    //让等待的api继续执行
                    synchronized (lock){
                        lock.notify();
                    }

                } catch (Exception e) {
                }
            }

            @Override
            public void onFailed(ARequest aRequest, com.aliyun.alink.linksdk.channel.core.base.AError aError) {
                System.out.println("mqtt dynamic registration failed");
                //让等待的api继续执行
                synchronized (lock){
                    lock.notify();
                }
            }

            @Override
            public boolean needUISafety() {
                return false;
            }
        });

        try {
            //等待下行报文,一般1s内就有回复
            synchronized (lock){
                lock.wait(3000);
            }

            //动态注册step4: 关闭动态注册的实例。
            //不要在LinkKit.getInstance().deviceDynamicRegister回调中执行下述函数,否则会报错
            LinkKit.getInstance().stopDeviceDynamicRegister(2000, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken iMqttToken) {
                    System.out.println("mqtt dynamic registration success");
                    //TODO: 在此处参考一机一密进行连云和初始化
                }

                @Override
                public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
                    System.out.println("mqtt dynamic registration failed");
                }
            });

        } catch (Exception e) {
        }

设置接入域名

示例代码如下:

// 设置MQTT请求域名LinkKitInitParams初始化参数
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig();
clientConfig.channelHost = "a18wP******.iot-as-mqtt.cn-shanghai.aliyuncs.com:8883";
linkKitInitParams.mqttClientConfig = clientConfig;           

参数说明:

参数

示例

说明

channelHost

a18wP******.iot-as-mqtt.cn-shanghai.aliyuncs.com:8883

设备的${接入域名}:${端口号}

  • 企业版实例和新版公共实例:在实例详情页面的开发配置面板,查看接入域名。

  • 旧版公共实例:接入域名格式为${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com:8883

新旧版公共实例和企业版实例、以及接入域名的更多信息,请参见查看实例终端节点

更多设置

您可以设置以下参数,实现设备接入相关的更多设置。

  • MQTT连接:

    配置项

    说明

    相关代码

    保活时间

    设置设备的保活时间。通过该设置实现设备与物联网平台保持长连接。

    MqttConfigure.setKeepAliveInterval(int interval);

    QoS等级

    设置QoS(Quality of Service)等级,即物联网平台与设备之间保证交付信息的协议。仅支持:

    • 0:最多一次。

    • 1:最少一次。

    - qos设置
    MqttPublishRequest request = new MqttPublishRequest();
    // 支持 0 和 1, 默认0
    request.qos = 0;
    request.isRPC = false;
    request.topic = topic.replace("request", "response");
    String resId = topic.substring(topic.indexOf("rrpc/request/")+13);
    request.msgId = resId;
    // TODO 用户根据实际情况填写,仅做参考
    request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":\"200\"" + ",\"data\":{} }";

    离线消息

    通过cleanSession,设置是否接收离线消息。

    /**
     * 设置MQTT初始化参数
     */
    IoTMqttClientConfig config = new IoTMqttClientConfig();
    config.productKey = deviceInfoData.productKey;
    config.deviceName = deviceInfoData.deviceName;
    config.deviceSecret = deviceInfoData.deviceSecret;
    config.channelHost = pk + ".iot-as-mqtt." + deviceInfoData.region + ".aliyuncs.com:1883";
    /**
     * 是否接受离线消息
     * 对应 receiveOfflineMsg = !cleanSession, 默认不接受离线消息
     */
    config.receiveOfflineMsg = false;
    params.mqttClientConfig = config;
  • 日志与Log4j支持:

    用户可以通过如下日志输出Debug日志:

    ALog.setLevel(ALog.LEVEL_DEBUG);
    MqttLogger.isLoggable = true;  // 输出底层MQTT库的全量日志, 默认关闭

    Java Link SDK从1.2.3.1版本起,提供了一个全量的拦截器,支持用户重写拦截器的log函数,实现自定义的日志处理。例如将日志通过Log4j工具持久化到文件中。

    日志输出示例代码:

            ALog.setLogDispatcher(new ILogDispatcher() {
                @Override
                public void log(int level, String prefix, String msg) {
                    switch (level){
                        case LEVEL_DEBUG:
                            System.out.println("debug:"+ prefix + msg);
                            break;
                        case LEVEL_INFO:
                            System.out.println("info:" + prefix + msg);
                            break;
                        case LEVEL_ERROR:
                            System.out.println("error:" + prefix + msg);
                            break;
                        case LEVEL_WARNING:
                            System.out.println("warnings:" + prefix + msg);
                            break;
                        default:
                            System.out.println("other:" + prefix + msg);
                    }
                }
            });
  • 连接状态与下行消息监听:

    如果需要监听设备的上下线信息,以及物联网平台下发的消息,可以设置以下监听器。

    IConnectNotifyListener notifyListener = new IConnectNotifyListener() {
        @Override
        public void onNotify(String connectId, String topic, AMessage aMessage) {
            // 物联网平台下行数据回调,包括connectId、连接类型、下行Topic和aMessage下行数据
            //String pushData = new String((byte[]) aMessage.data);
            // pushData示例  {"method":"thing.service.test_service","id":"123374967","params":{"vv":60},"version":"1.0.0"}
            // 上一行代码表示method服务类型以及params下推数据内容    
    }
        @Override
        public boolean shouldHandle(String connectId, String topic) {
            // 选择是否不处理某个Topic的下行数据
            // 如果不处理某个Topic,则onNotify不会收到对应Topic的下行数据
            return true; //TODO 根据实际情况,编写要处理的监听逻辑。
        }
        @Override
        public void onConnectStateChange(String connectId, ConnectState connectState) {
            // 对应连接类型的连接状态变化回调,具体连接状态参考SDK ConnectState
            //当SDK因网络波动断开连接时,会自动尝试重连,重试的间隔是1s、2s、4s、8s...128s...128s, 到了最大间隔128s后,会一直以128s为间隔重连直到连云成功。
        }
    }
    // 注册下行监听,包括长连接的状态和下行的数据
    LinkKit.getInstance().registerOnNotifyListener(notifyListener);
                
  • 反初始化:

    如果需要注销初始化,请参考如下示例代码。

    // 取消注册 notifyListener,notifyListener对象需和注册的时候是同一个对象
    LinkKit.getInstance().unRegisterOnNotifyListener(notifyListener);
    LinkKit.getInstance().deinit();